Commit 33b5e8c0 authored by Leon Roy's avatar Leon Roy Committed by leonroy

OF-651 - Monitoring plugin should have an option to purge and restrict

Restriction implemented, purging left

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13650 b35dd754-fafc-0310-a699-88a17e54d16e
parent 1a4803a6
...@@ -76,6 +76,10 @@ archive.settings.idle.time = Idle Time ...@@ -76,6 +76,10 @@ archive.settings.idle.time = Idle Time
archive.settings.idle.time.description = The number of minutes a conversation can be idle before it's ended. archive.settings.idle.time.description = The number of minutes a conversation can be idle before it's ended.
archive.settings.max.time = Max Time archive.settings.max.time = Max Time
archive.settings.max.time.description = The maximum number of minutes a conversation can last before it's ended. archive.settings.max.time.description = The maximum number of minutes a conversation can last before it's ended.
archive.settings.max.age = Max Message Age
archive.settings.max.age.description = The maximum number of days to keep messages before purging them from the database.
archive.settings.max.retrievable = Retrievable Messages
archive.settings.max.retrievable.description = The number of days worth of messages a user is allowed to retrieve.
archive.settings.index.settings = Index Settings archive.settings.index.settings = Index Settings
archive.settings.index.settings.description = View and/or rebuild the current Search Index. archive.settings.index.settings.description = View and/or rebuild the current Search Index.
archive.settings.current.index = Current Search Index archive.settings.current.index = Current Search Index
......
...@@ -50,6 +50,10 @@ archive.settings.idle.time = Doba ne\u010dinnosti ...@@ -50,6 +50,10 @@ archive.settings.idle.time = Doba ne\u010dinnosti
archive.settings.idle.time.description = Po\u010det minut b\u011bhem kter\u00fdch m\u016f\u017ee b\u00fdt konverzace ne\u010dinn\u00e1 ne\u017e bude ukon\u010dena. archive.settings.idle.time.description = Po\u010det minut b\u011bhem kter\u00fdch m\u016f\u017ee b\u00fdt konverzace ne\u010dinn\u00e1 ne\u017e bude ukon\u010dena.
archive.settings.max.time = Maxim\u00e1ln\u00ed \u010das archive.settings.max.time = Maxim\u00e1ln\u00ed \u010das
archive.settings.max.time.description = Maxim\u00e1ln\u00ed po\u010det minut trv\u00e1n\u00ed konverzace ne\u017e bude ukon\u010dena. archive.settings.max.time.description = Maxim\u00e1ln\u00ed po\u010det minut trv\u00e1n\u00ed konverzace ne\u017e bude ukon\u010dena.
archive.settings.max.age = Max Message Age
archive.settings.max.age.description = The maximum number of days to keep messages before purging them from the database.
archive.settings.max.retrievable = Retrievable Messages
archive.settings.max.retrievable.description = The number of days worth of messages a user is allowed to retrieve.
archive.settings.index.settings = Nastaven\u00ed index\u016f archive.settings.index.settings = Nastaven\u00ed index\u016f
archive.settings.index.settings.description = Prohl\u00ed\u017een\u00ed a/nebo obnova aktu\u00e1ln\u00edho vyhled\u00e1vac\u00edho indexu. archive.settings.index.settings.description = Prohl\u00ed\u017een\u00ed a/nebo obnova aktu\u00e1ln\u00edho vyhled\u00e1vac\u00edho indexu.
archive.settings.current.index = Aktu\u00e1ln\u00ed vyhled\u00e1vac\u00ed index archive.settings.current.index = Aktu\u00e1ln\u00ed vyhled\u00e1vac\u00ed index
......
...@@ -50,6 +50,10 @@ archive.settings.idle.time = Tiempo Ocioso ...@@ -50,6 +50,10 @@ archive.settings.idle.time = Tiempo Ocioso
archive.settings.idle.time.description = El n\u00famero de minutos una conversaci\u00f3n puede estar libre antes de ser finalizada. archive.settings.idle.time.description = El n\u00famero de minutos una conversaci\u00f3n puede estar libre antes de ser finalizada.
archive.settings.max.time = Tiempo M\u00e1ximo archive.settings.max.time = Tiempo M\u00e1ximo
archive.settings.max.time.description = El m\u00e1ximo n\u00famero de minutos una conversacion puede durar antes de ser finalizada. archive.settings.max.time.description = El m\u00e1ximo n\u00famero de minutos una conversacion puede durar antes de ser finalizada.
archive.settings.max.age = Max Message Age
archive.settings.max.age.description = The maximum number of days to keep messages before purging them from the database.
archive.settings.max.retrievable = Max Retrievable Messages
archive.settings.max.retrievable.description = The number of days worth of messages a user is allowed to retrieve.
archive.settings.index.settings = Seteos de Indices archive.settings.index.settings = Seteos de Indices
archive.settings.index.settings.description = Ver y/o reconstruir el \u00edndice de b\u00fasqueda actual. archive.settings.index.settings.description = Ver y/o reconstruir el \u00edndice de b\u00fasqueda actual.
archive.settings.current.index = Indice de B\u00fasqueda Actual archive.settings.current.index = Indice de B\u00fasqueda Actual
......
...@@ -110,6 +110,10 @@ archive.settings.index.settings=Param\u00e8tres d'Indexage ...@@ -110,6 +110,10 @@ archive.settings.index.settings=Param\u00e8tres d'Indexage
archive.settings.index.settings.description=Voir et/ou reconstruire les indexes de recherche. archive.settings.index.settings.description=Voir et/ou reconstruire les indexes de recherche.
archive.settings.max.time=Temps Max archive.settings.max.time=Temps Max
archive.settings.max.time.description=Le nombre de minutes maximum d'une conversation avant qu'elle ne soit coup\u00e9e automatiquement. archive.settings.max.time.description=Le nombre de minutes maximum d'une conversation avant qu'elle ne soit coup\u00e9e automatiquement.
archive.settings.max.age = Max Message Age
archive.settings.max.age.description = The maximum number of days to keep messages before purging them from the database.
archive.settings.max.retrievable = Retrievable Messages
archive.settings.max.retrievable.description = The number of days worth of messages a user is allowed to retrieve.
archive.settings.message.count=Nombre de messages archiv\u00e9s archive.settings.message.count=Nombre de messages archiv\u00e9s
archive.settings.message.count.description=Le nombre total de messages archiv\u00e9s. archive.settings.message.count.description=Le nombre total de messages archiv\u00e9s.
archive.settings.message.metadata.description=Activer ou D\u00e9sactiver l'archivage des messages et/ou metadata. archive.settings.message.metadata.description=Activer ou D\u00e9sactiver l'archivage des messages et/ou metadata.
......
...@@ -74,6 +74,10 @@ archive.settings.idle.time = Tempo Ocioso ...@@ -74,6 +74,10 @@ archive.settings.idle.time = Tempo Ocioso
archive.settings.idle.time.description = N\u00famero de minutos que a conversa pode estar ociosa antes de ser encerrada. archive.settings.idle.time.description = N\u00famero de minutos que a conversa pode estar ociosa antes de ser encerrada.
archive.settings.max.time = Tempo M\u00e1ximo archive.settings.max.time = Tempo M\u00e1ximo
archive.settings.max.time.description = N\u00famero m\u00e1ximo de minutos que a conversa pode durar antes de ser encerrada. archive.settings.max.time.description = N\u00famero m\u00e1ximo de minutos que a conversa pode durar antes de ser encerrada.
archive.settings.max.age = Max Message Age
archive.settings.max.age.description = The maximum number of days to keep messages before purging them from the database.
archive.settings.max.retrievable = Retrievable Messages
archive.settings.max.retrievable.description = The number of days worth of messages a user is allowed to retrieve.
archive.settings.index.settings = Ajustes de \u00edndice archive.settings.index.settings = Ajustes de \u00edndice
archive.settings.index.settings.description = Ver e/ou refazer o \u00edndice de Buscas atual. archive.settings.index.settings.description = Ver e/ou refazer o \u00edndice de Buscas atual.
archive.settings.current.index = \u00edndice de Buscas Atual archive.settings.current.index = \u00edndice de Buscas Atual
......
...@@ -71,6 +71,10 @@ archive.settings.idle.time = \u7a7a\u95f2\u65f6\u95f4 ...@@ -71,6 +71,10 @@ archive.settings.idle.time = \u7a7a\u95f2\u65f6\u95f4
archive.settings.idle.time.description = \u4f1a\u8bdd\u7ed3\u675f\u540e\u7a7a\u95f2\u7684\u65f6\u95f4 archive.settings.idle.time.description = \u4f1a\u8bdd\u7ed3\u675f\u540e\u7a7a\u95f2\u7684\u65f6\u95f4
archive.settings.max.time = \u6700\u5927\u65f6\u95f4 archive.settings.max.time = \u6700\u5927\u65f6\u95f4
archive.settings.max.time.description = \u4f1a\u8bdd\u7ed3\u675f\u524d\u53ef\u6301\u7eed\u7684\u6700\u5927\u65f6\u95f4 archive.settings.max.time.description = \u4f1a\u8bdd\u7ed3\u675f\u524d\u53ef\u6301\u7eed\u7684\u6700\u5927\u65f6\u95f4
archive.settings.max.age = Max Message Age
archive.settings.max.age.description = The maximum number of days to keep messages before purging them from the database.
archive.settings.max.retrievable = Retrievable Messages
archive.settings.max.retrievable.description = The number of days worth of messages a user is allowed to retrieve.
archive.settings.index.settings = \u7d22\u5f15\u8bbe\u7f6e archive.settings.index.settings = \u7d22\u5f15\u8bbe\u7f6e
archive.settings.index.settings.description = \u67e5\u770b\u6216\u91cd\u5efa\u5f53\u524d\u7684\u641c\u7d22\u7d22\u5f15 archive.settings.index.settings.description = \u67e5\u770b\u6216\u91cd\u5efa\u5f53\u524d\u7684\u641c\u7d22\u7d22\u5f15
archive.settings.current.index = \u5f53\u524d\u7684\u641c\u7d22\u7d22\u5f15 archive.settings.current.index = \u5f53\u524d\u7684\u641c\u7d22\u7d22\u5f15
......
...@@ -4,6 +4,7 @@ import java.sql.Connection; ...@@ -4,6 +4,7 @@ import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.text.DateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
...@@ -13,6 +14,9 @@ import java.util.HashSet; ...@@ -13,6 +14,9 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import org.jivesoftware.database.DbConnectionManager; import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.archive.ConversationManager;
import org.jivesoftware.util.JiveConstants;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
...@@ -30,23 +34,11 @@ import com.reucon.openfire.plugin.archive.xep0059.XmppResultSet; ...@@ -30,23 +34,11 @@ import com.reucon.openfire.plugin.archive.xep0059.XmppResultSet;
public class JdbcPersistenceManager implements PersistenceManager { public class JdbcPersistenceManager implements PersistenceManager {
public static final int DEFAULT_MAX = 1000; public static final int DEFAULT_MAX = 1000;
public static final String SELECT_MESSAGES_BY_CONVERSATION = "SELECT DISTINCT " public static final String SELECT_MESSAGES_BY_CONVERSATION = "SELECT DISTINCT " + "ofConversation.conversationID, " + "ofConversation.room, "
+ "ofConversation.conversationID, " + "ofConversation.isExternal, " + "ofConversation.startDate, " + "ofConversation.lastActivity, " + "ofConversation.messageCount, "
+ "ofConversation.room, " + "ofConParticipant.joinedDate, " + "ofConParticipant.leftDate, " + "ofConParticipant.bareJID, " + "ofConParticipant.jidResource, "
+ "ofConversation.isExternal, " + "ofConParticipant.nickname, " + "ofMessageArchive.fromJID, " + "ofMessageArchive.toJID, " + "ofMessageArchive.sentDate, "
+ "ofConversation.startDate, " + "ofMessageArchive.body " + "FROM ofConversation "
+ "ofConversation.lastActivity, "
+ "ofConversation.messageCount, "
+ "ofConParticipant.joinedDate, "
+ "ofConParticipant.leftDate, "
+ "ofConParticipant.bareJID, "
+ "ofConParticipant.jidResource, "
+ "ofConParticipant.nickname, "
+ "ofMessageArchive.fromJID, "
+ "ofMessageArchive.toJID, "
+ "ofMessageArchive.sentDate, "
+ "ofMessageArchive.body "
+ "FROM ofConversation "
+ "INNER JOIN ofConParticipant ON ofConversation.conversationID = ofConParticipant.conversationID " + "INNER JOIN ofConParticipant ON ofConversation.conversationID = ofConParticipant.conversationID "
+ "INNER JOIN ofMessageArchive ON ofConParticipant.conversationID = ofMessageArchive.conversationID " + "INNER JOIN ofMessageArchive ON ofConParticipant.conversationID = ofMessageArchive.conversationID "
+ "WHERE ofConversation.conversationID = ? AND ofConParticipant.bareJID = ? ORDER BY ofMessageArchive.sentDate"; + "WHERE ofConversation.conversationID = ? AND ofConParticipant.bareJID = ? ORDER BY ofMessageArchive.sentDate";
...@@ -55,23 +47,11 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -55,23 +47,11 @@ public class JdbcPersistenceManager implements PersistenceManager {
// "SELECT messageId,time,direction,type,subject,body " // "SELECT messageId,time,direction,type,subject,body "
// + "FROM archiveMessages WHERE conversationId = ? ORDER BY time"; // + "FROM archiveMessages WHERE conversationId = ? ORDER BY time";
public static final String SELECT_CONVERSATIONS = "SELECT DISTINCT " public static final String SELECT_CONVERSATIONS = "SELECT DISTINCT " + "ofConversation.conversationID, " + "ofConversation.room, "
+ "ofConversation.conversationID, " + "ofConversation.isExternal, " + "ofConversation.startDate, " + "ofConversation.lastActivity, " + "ofConversation.messageCount, "
+ "ofConversation.room, " + "ofConParticipant.joinedDate, " + "ofConParticipant.leftDate, " + "ofConParticipant.bareJID, " + "ofConParticipant.jidResource, "
+ "ofConversation.isExternal, " + "ofConParticipant.nickname, " + "ofMessageArchive.fromJID, " + "ofMessageArchive.toJID, " + "ofMessageArchive.sentDate, "
+ "ofConversation.startDate, " + "ofMessageArchive.body " + "FROM ofConversation "
+ "ofConversation.lastActivity, "
+ "ofConversation.messageCount, "
+ "ofConParticipant.joinedDate, "
+ "ofConParticipant.leftDate, "
+ "ofConParticipant.bareJID, "
+ "ofConParticipant.jidResource, "
+ "ofConParticipant.nickname, "
+ "ofMessageArchive.fromJID, "
+ "ofMessageArchive.toJID, "
+ "ofMessageArchive.sentDate, "
+ "ofMessageArchive.body "
+ "FROM ofConversation "
+ "INNER JOIN ofConParticipant ON ofConversation.conversationID = ofConParticipant.conversationID " + "INNER JOIN ofConParticipant ON ofConversation.conversationID = ofConParticipant.conversationID "
+ "INNER JOIN ofMessageArchive ON ofConParticipant.conversationID = ofMessageArchive.conversationID"; + "INNER JOIN ofMessageArchive ON ofConParticipant.conversationID = ofMessageArchive.conversationID";
...@@ -101,23 +81,11 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -101,23 +81,11 @@ public class JdbcPersistenceManager implements PersistenceManager {
public static final String CONVERSATION_WITH_JID = "(ofMessageArchive.toJID = ? OR ofMessageArchive.fromJID = ?)"; public static final String CONVERSATION_WITH_JID = "(ofMessageArchive.toJID = ? OR ofMessageArchive.fromJID = ?)";
// public static final String CONVERSATION_WITH_JID = "c.withJid"; // public static final String CONVERSATION_WITH_JID = "c.withJid";
public static final String SELECT_ACTIVE_CONVERSATIONS = "SELECT DISTINCT " public static final String SELECT_ACTIVE_CONVERSATIONS = "SELECT DISTINCT " + "ofConversation.conversationID, " + "ofConversation.room, "
+ "ofConversation.conversationID, " + "ofConversation.isExternal, " + "ofConversation.startDate, " + "ofConversation.lastActivity, " + "ofConversation.messageCount, "
+ "ofConversation.room, " + "ofConParticipant.joinedDate, " + "ofConParticipant.leftDate, " + "ofConParticipant.bareJID, " + "ofConParticipant.jidResource, "
+ "ofConversation.isExternal, " + "ofConParticipant.nickname, " + "ofMessageArchive.fromJID, " + "ofMessageArchive.toJID, " + "ofMessageArchive.sentDate, "
+ "ofConversation.startDate, " + "ofMessageArchive.body " + "FROM ofConversation "
+ "ofConversation.lastActivity, "
+ "ofConversation.messageCount, "
+ "ofConParticipant.joinedDate, "
+ "ofConParticipant.leftDate, "
+ "ofConParticipant.bareJID, "
+ "ofConParticipant.jidResource, "
+ "ofConParticipant.nickname, "
+ "ofMessageArchive.fromJID, "
+ "ofMessageArchive.toJID, "
+ "ofMessageArchive.sentDate, "
+ "ofMessageArchive.body "
+ "FROM ofConversation "
+ "INNER JOIN ofConParticipant ON ofConversation.conversationID = ofConParticipant.conversationID " + "INNER JOIN ofConParticipant ON ofConversation.conversationID = ofConParticipant.conversationID "
+ "INNER JOIN ofMessageArchive ON ofConParticipant.conversationID = ofMessageArchive.conversationID " + "INNER JOIN ofMessageArchive ON ofConParticipant.conversationID = ofMessageArchive.conversationID "
+ "WHERE ofConversation.lastActivity > ?"; + "WHERE ofConversation.lastActivity > ?";
...@@ -127,12 +95,8 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -127,12 +95,8 @@ public class JdbcPersistenceManager implements PersistenceManager {
// + " c.subject,c.thread " // + " c.subject,c.thread "
// + "FROM archiveConversations AS c WHERE c.endTime > ?"; // + "FROM archiveConversations AS c WHERE c.endTime > ?";
public static final String SELECT_PARTICIPANTS_BY_CONVERSATION = "SELECT DISTINCT " public static final String SELECT_PARTICIPANTS_BY_CONVERSATION = "SELECT DISTINCT " + "ofConversation.conversationID, "
+ "ofConversation.conversationID, " + "ofConversation.startDate, " + "ofConversation.lastActivity, " + "ofConParticipant.bareJID " + "FROM ofConversation "
+ "ofConversation.startDate, "
+ "ofConversation.lastActivity, "
+ "ofConParticipant.bareJID "
+ "FROM ofConversation "
+ "INNER JOIN ofConParticipant ON ofConversation.conversationID = ofConParticipant.conversationID " + "INNER JOIN ofConParticipant ON ofConversation.conversationID = ofConParticipant.conversationID "
+ "INNER JOIN ofMessageArchive ON ofConParticipant.conversationID = ofMessageArchive.conversationID " + "INNER JOIN ofMessageArchive ON ofConParticipant.conversationID = ofMessageArchive.conversationID "
+ "WHERE ofConversation.conversationID = ? ORDER BY ofConversation.startDate"; + "WHERE ofConversation.conversationID = ? ORDER BY ofConversation.startDate";
...@@ -159,20 +123,32 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -159,20 +123,32 @@ public class JdbcPersistenceManager implements PersistenceManager {
return false; return false;
} }
public boolean createParticipant(Participant participant, public boolean createParticipant(Participant participant, Long conversationId) {
Long conversationId) {
return false; return false;
} }
public List<Conversation> findConversations(String[] participants, public List<Conversation> findConversations(String[] participants, Date startDate, Date endDate) {
Date startDate, Date endDate) {
final List<Conversation> conversations = new ArrayList<Conversation>(); final List<Conversation> conversations = new ArrayList<Conversation>();
return conversations; return conversations;
} }
public Collection<Conversation> findConversations(Date startDate, public Date getAuditedStartDate(Date startDate) {
Date endDate, String ownerJid, String withJid, long maxRetrievable = JiveGlobals.getIntProperty("conversation.maxRetrievable", ConversationManager.DEFAULT_MAX_RETRIEVABLE)
XmppResultSet xmppResultSet) { * JiveConstants.DAY;
Date result = null;
if (maxRetrievable > 0) {
Date now = new Date();
Date maxRetrievableDate = new Date(now.getTime() - maxRetrievable);
if (startDate == null) {
result = maxRetrievableDate;
} else if (startDate.before(maxRetrievableDate)) {
result = maxRetrievableDate;
}
}
return result;
}
public Collection<Conversation> findConversations(Date startDate, Date endDate, String ownerJid, String withJid, XmppResultSet xmppResultSet) {
final HashMap<Long, Conversation> conversations; final HashMap<Long, Conversation> conversations;
final StringBuilder querySB; final StringBuilder querySB;
final StringBuilder whereSB; final StringBuilder whereSB;
...@@ -184,6 +160,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -184,6 +160,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
whereSB = new StringBuilder(); whereSB = new StringBuilder();
limitSB = new StringBuilder(); limitSB = new StringBuilder();
startDate = getAuditedStartDate(startDate);
if (startDate != null) { if (startDate != null) {
appendWhere(whereSB, CONVERSATION_START_TIME, " >= ?"); appendWhere(whereSB, CONVERSATION_START_TIME, " >= ?");
} }
...@@ -199,22 +176,16 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -199,22 +176,16 @@ public class JdbcPersistenceManager implements PersistenceManager {
if (xmppResultSet != null) { if (xmppResultSet != null) {
Integer firstIndex = null; Integer firstIndex = null;
int max = xmppResultSet.getMax() != null ? xmppResultSet.getMax() int max = xmppResultSet.getMax() != null ? xmppResultSet.getMax() : DEFAULT_MAX;
: DEFAULT_MAX;
xmppResultSet.setCount(countConversations(startDate, endDate, xmppResultSet.setCount(countConversations(startDate, endDate, ownerJid, withJid, whereSB.toString()));
ownerJid, withJid, whereSB.toString()));
if (xmppResultSet.getIndex() != null) { if (xmppResultSet.getIndex() != null) {
firstIndex = xmppResultSet.getIndex(); firstIndex = xmppResultSet.getIndex();
} else if (xmppResultSet.getAfter() != null) { } else if (xmppResultSet.getAfter() != null) {
firstIndex = countConversationsBefore(startDate, endDate, firstIndex = countConversationsBefore(startDate, endDate, ownerJid, withJid, xmppResultSet.getAfter(), whereSB.toString());
ownerJid, withJid, xmppResultSet.getAfter(),
whereSB.toString());
firstIndex += 1; firstIndex += 1;
} else if (xmppResultSet.getBefore() != null) { } else if (xmppResultSet.getBefore() != null) {
firstIndex = countConversationsBefore(startDate, endDate, firstIndex = countConversationsBefore(startDate, endDate, ownerJid, withJid, xmppResultSet.getBefore(), whereSB.toString());
ownerJid, withJid, xmppResultSet.getBefore(),
whereSB.toString());
firstIndex -= max; firstIndex -= max;
if (firstIndex < 0) { if (firstIndex < 0) {
firstIndex = 0; firstIndex = 0;
...@@ -239,11 +210,9 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -239,11 +210,9 @@ public class JdbcPersistenceManager implements PersistenceManager {
try { try {
con = DbConnectionManager.getConnection(); con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(querySB.toString()); pstmt = con.prepareStatement(querySB.toString());
bindConversationParameters(startDate, endDate, ownerJid, withJid, bindConversationParameters(startDate, endDate, ownerJid, withJid, pstmt);
pstmt);
rs = pstmt.executeQuery(); rs = pstmt.executeQuery();
Log.debug("findConversations: SELECT_CONVERSATIONS: " Log.debug("findConversations: SELECT_CONVERSATIONS: " + pstmt.toString());
+ pstmt.toString());
while (rs.next()) { while (rs.next()) {
Conversation conv = extractConversation(rs); Conversation conv = extractConversation(rs);
conversations.put(conv.getId(), conv); conversations.put(conv.getId(), conv);
...@@ -255,12 +224,10 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -255,12 +224,10 @@ public class JdbcPersistenceManager implements PersistenceManager {
} }
if (xmppResultSet != null && conversations.size() > 0) { if (xmppResultSet != null && conversations.size() > 0) {
ArrayList<Long> sortedConvKeys = new ArrayList<Long>( ArrayList<Long> sortedConvKeys = new ArrayList<Long>(conversations.keySet());
conversations.keySet());
Collections.sort(sortedConvKeys); Collections.sort(sortedConvKeys);
xmppResultSet.setFirst(sortedConvKeys.get(0)); xmppResultSet.setFirst(sortedConvKeys.get(0));
xmppResultSet xmppResultSet.setLast(sortedConvKeys.get(sortedConvKeys.size() - 1));
.setLast(sortedConvKeys.get(sortedConvKeys.size() - 1));
} }
return conversations.values(); return conversations.values();
} }
...@@ -275,8 +242,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -275,8 +242,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
} }
} }
private int countConversations(Date startDate, Date endDate, private int countConversations(Date startDate, Date endDate, String ownerJid, String withJid, String whereClause) {
String ownerJid, String withJid, String whereClause) {
StringBuilder querySB; StringBuilder querySB;
querySB = new StringBuilder(COUNT_CONVERSATIONS); querySB = new StringBuilder(COUNT_CONVERSATIONS);
...@@ -290,8 +256,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -290,8 +256,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
try { try {
con = DbConnectionManager.getConnection(); con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(querySB.toString()); pstmt = con.prepareStatement(querySB.toString());
bindConversationParameters(startDate, endDate, ownerJid, withJid, bindConversationParameters(startDate, endDate, ownerJid, withJid, pstmt);
pstmt);
rs = pstmt.executeQuery(); rs = pstmt.executeQuery();
if (rs.next()) { if (rs.next()) {
return rs.getInt(1); return rs.getInt(1);
...@@ -306,8 +271,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -306,8 +271,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
} }
} }
private int countConversationsBefore(Date startDate, Date endDate, private int countConversationsBefore(Date startDate, Date endDate, String ownerJid, String withJid, Long before, String whereClause) {
String ownerJid, String withJid, Long before, String whereClause) {
StringBuilder querySB; StringBuilder querySB;
querySB = new StringBuilder(COUNT_CONVERSATIONS); querySB = new StringBuilder(COUNT_CONVERSATIONS);
...@@ -325,8 +289,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -325,8 +289,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
int parameterIndex; int parameterIndex;
con = DbConnectionManager.getConnection(); con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(querySB.toString()); pstmt = con.prepareStatement(querySB.toString());
parameterIndex = bindConversationParameters(startDate, endDate, parameterIndex = bindConversationParameters(startDate, endDate, ownerJid, withJid, pstmt);
ownerJid, withJid, pstmt);
pstmt.setLong(parameterIndex, before); pstmt.setLong(parameterIndex, before);
rs = pstmt.executeQuery(); rs = pstmt.executeQuery();
if (rs.next()) { if (rs.next()) {
...@@ -342,8 +305,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -342,8 +305,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
} }
} }
private int bindConversationParameters(Date startDate, Date endDate, private int bindConversationParameters(Date startDate, Date endDate, String ownerJid, String withJid, PreparedStatement pstmt)
String ownerJid, String withJid, PreparedStatement pstmt)
throws SQLException { throws SQLException {
int parameterIndex = 1; int parameterIndex = 1;
...@@ -363,8 +325,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -363,8 +325,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
return parameterIndex; return parameterIndex;
} }
public Collection<Conversation> getActiveConversations( public Collection<Conversation> getActiveConversations(int conversationTimeout) {
int conversationTimeout) {
final Collection<Conversation> conversations; final Collection<Conversation> conversations;
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
...@@ -438,8 +399,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -438,8 +399,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
return conversations; return conversations;
} }
public Conversation getConversation(String ownerJid, String withJid, public Conversation getConversation(String ownerJid, String withJid, Date start) {
Date start) {
return getConversation(null, ownerJid, withJid, start); return getConversation(null, ownerJid, withJid, start);
} }
...@@ -447,8 +407,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -447,8 +407,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
return getConversation(conversationId, null, null, null); return getConversation(conversationId, null, null, null);
} }
private Conversation getConversation(Long conversationId, String ownerJid, private Conversation getConversation(Long conversationId, String ownerJid, String withJid, Date start) {
String withJid, Date start) {
Conversation conversation = null; Conversation conversation = null;
StringBuilder querySB; StringBuilder querySB;
...@@ -490,8 +449,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -490,8 +449,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
} }
} }
rs = pstmt.executeQuery(); rs = pstmt.executeQuery();
Log.debug("getConversation: SELECT_CONVERSATIONS: " Log.debug("getConversation: SELECT_CONVERSATIONS: " + pstmt.toString());
+ pstmt.toString());
if (rs.next()) { if (rs.next()) {
conversation = extractConversation(rs); conversation = extractConversation(rs);
} else { } else {
...@@ -505,8 +463,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -505,8 +463,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
pstmt.setLong(1, conversation.getId()); pstmt.setLong(1, conversation.getId());
rs = pstmt.executeQuery(); rs = pstmt.executeQuery();
Log.debug("getConversation: SELECT_PARTICIPANTS_BY_CONVERSATION: " Log.debug("getConversation: SELECT_PARTICIPANTS_BY_CONVERSATION: " + pstmt.toString());
+ pstmt.toString());
while (rs.next()) { while (rs.next()) {
for (Participant participant : extractParticipant(rs)) { for (Participant participant : extractParticipant(rs)) {
...@@ -522,8 +479,7 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -522,8 +479,7 @@ public class JdbcPersistenceManager implements PersistenceManager {
pstmt.setString(2, conversation.getOwnerJid()); pstmt.setString(2, conversation.getOwnerJid());
rs = pstmt.executeQuery(); rs = pstmt.executeQuery();
Log.debug("getConversation: SELECT_MESSAGES_BY_CONVERSATION: " Log.debug("getConversation: SELECT_MESSAGES_BY_CONVERSATION: " + pstmt.toString());
+ pstmt.toString());
while (rs.next()) { while (rs.next()) {
ArchivedMessage message; ArchivedMessage message;
...@@ -567,14 +523,12 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -567,14 +523,12 @@ public class JdbcPersistenceManager implements PersistenceManager {
if (bareJid != null && fromJid != null && toJid != null) { if (bareJid != null && fromJid != null && toJid != null) {
if (bareJid.equals(fromJid)) { if (bareJid.equals(fromJid)) {
/* /*
* if message from me to withJid then it is to the withJid * if message from me to withJid then it is to the withJid participant
* participant
*/ */
direction = Direction.to; direction = Direction.to;
} else { } else {
/* /*
* if message to me from withJid then it is from the withJid * if message to me from withJid then it is from the withJid participant
* participant
*/ */
direction = Direction.from; direction = Direction.from;
} }
...@@ -594,14 +548,12 @@ public class JdbcPersistenceManager implements PersistenceManager { ...@@ -594,14 +548,12 @@ public class JdbcPersistenceManager implements PersistenceManager {
String subject = null; String subject = null;
String thread = String.valueOf(id); String thread = String.valueOf(id);
conversation = new Conversation(startDate, ownerJid, ownerResource, conversation = new Conversation(startDate, ownerJid, ownerResource, withJid, withResource, subject, thread);
withJid, withResource, subject, thread);
conversation.setId(id); conversation.setId(id);
return conversation; return conversation;
} }
private Collection<Participant> extractParticipant(ResultSet rs) private Collection<Participant> extractParticipant(ResultSet rs) throws SQLException {
throws SQLException {
Collection<Participant> participants = new HashSet<Participant>(); Collection<Participant> participants = new HashSet<Participant>();
Date startDate = millisToDate(rs.getLong("startDate")); Date startDate = millisToDate(rs.getLong("startDate"));
......
...@@ -67,1003 +67,1044 @@ import org.xmpp.packet.JID; ...@@ -67,1003 +67,1044 @@ import org.xmpp.packet.JID;
import org.xmpp.packet.Message; import org.xmpp.packet.Message;
/** /**
* Manages all conversations in the system. Optionally, conversations (messages plus * Manages all conversations in the system. Optionally, conversations (messages plus meta-data) can be archived to the database. Archiving of
* meta-data) can be archived to the database. Archiving of conversation data is * conversation data is enabled by default, but can be disabled by setting "conversation.metadataArchiving" to <tt>false</tt>. Archiving of messages
* enabled by default, but can be disabled by setting "conversation.metadataArchiving" to * in a conversation is disabled by default, but can be enabled by setting "conversation.messageArchiving" to <tt>true</tt>.
* <tt>false</tt>. Archiving of messages in a conversation is disabled by default, but * <p>
* can be enabled by setting "conversation.messageArchiving" to <tt>true</tt>.<p> *
* * When running in a cluster only the senior cluster member will keep track of the active conversations. Other cluster nodes will forward conversation
* When running in a cluster only the senior cluster member will keep track of the active * events that occurred in the local node to the senior cluster member. If the senior cluster member goes down then current conversations will be
* conversations. Other cluster nodes will forward conversation events that occurred in the * terminated and if users keep sending messages between them then new conversations will be created.
* local node to the senior cluster member. If the senior cluster member goes down then *
* current conversations will be terminated and if users keep sending messages between them
* then new conversations will be created.
*
* @author Matt Tucker * @author Matt Tucker
*/ */
public class ConversationManager implements Startable, ComponentEventListener { public class ConversationManager implements Startable, ComponentEventListener {
private static final Logger Log = LoggerFactory.getLogger(ConversationManager.class); private static final Logger Log = LoggerFactory.getLogger(ConversationManager.class);
private static final String UPDATE_CONVERSATION = private static final String UPDATE_CONVERSATION = "UPDATE ofConversation SET lastActivity=?, messageCount=? WHERE conversationID=?";
"UPDATE ofConversation SET lastActivity=?, messageCount=? WHERE conversationID=?"; private static final String UPDATE_PARTICIPANT = "UPDATE ofConParticipant SET leftDate=? WHERE conversationID=? AND bareJID=? AND jidResource=? AND joinedDate=?";
private static final String UPDATE_PARTICIPANT = private static final String INSERT_MESSAGE = "INSERT INTO ofMessageArchive(conversationID, fromJID, fromJIDResource, toJID, toJIDResource, sentDate, body) "
"UPDATE ofConParticipant SET leftDate=? WHERE conversationID=? AND bareJID=? AND jidResource=? AND joinedDate=?"; + "VALUES (?,?,?,?,?,?,?)";
private static final String INSERT_MESSAGE = private static final String CONVERSATION_COUNT = "SELECT COUNT(*) FROM ofConversation";
"INSERT INTO ofMessageArchive(conversationID, fromJID, fromJIDResource, toJID, toJIDResource, sentDate, body) " + private static final String MESSAGE_COUNT = "SELECT COUNT(*) FROM ofMessageArchive";
"VALUES (?,?,?,?,?,?,?)";
private static final String CONVERSATION_COUNT = private static final int DEFAULT_IDLE_TIME = 10;
"SELECT COUNT(*) FROM ofConversation"; private static final int DEFAULT_MAX_TIME = 60;
private static final String MESSAGE_COUNT =
"SELECT COUNT(*) FROM ofMessageArchive"; public static final int DEFAULT_MAX_RETRIEVABLE = 0;
private static final int DEFAULT_MAX_AGE = 0;
private static final int DEFAULT_IDLE_TIME = 10;
private static final int DEFAULT_MAX_TIME = 60; public static final String CONVERSATIONS_KEY = "conversations";
public static final String CONVERSATIONS_KEY = "conversations"; private ConversationEventsQueue conversationEventsQueue;
private TaskEngine taskEngine;
private ConversationEventsQueue conversationEventsQueue;
private TaskEngine taskEngine; private Map<String, Conversation> conversations = new ConcurrentHashMap<String, Conversation>();
private boolean metadataArchivingEnabled;
private Map<String, Conversation> conversations = new ConcurrentHashMap<String, Conversation>(); /**
private boolean metadataArchivingEnabled; * Flag that indicates if messages of one-to-one chats should be archived.
/** */
* Flag that indicates if messages of one-to-one chats should be archived. private boolean messageArchivingEnabled;
*/ /**
private boolean messageArchivingEnabled; * Flag that indicates if messages of group chats (in MUC rooms) should be archived.
/** */
* Flag that indicates if messages of group chats (in MUC rooms) should be archived. private boolean roomArchivingEnabled;
*/ /**
private boolean roomArchivingEnabled; * List of room names to archive. When list is empty then all rooms are archived (if roomArchivingEnabled is enabled).
/** */
* List of room names to archive. When list is empty then all rooms are archived (if private Collection<String> roomsArchived;
* roomArchivingEnabled is enabled). private long idleTime;
*/ private long maxTime;
private Collection<String> roomsArchived; private long maxAge;
private long idleTime; private long maxRetrievable;
private long maxTime; private PropertyEventListener propertyListener;
private PropertyEventListener propertyListener;
private Queue<Conversation> conversationQueue;
private Queue<Conversation> conversationQueue; private Queue<ArchivedMessage> messageQueue;
private Queue<ArchivedMessage> messageQueue; /**
/** * Queue of participants that joined or left a conversation. This queue is processed by the ArchivingTask.
* Queue of participants that joined or left a conversation. This queue is processed by the */
* ArchivingTask. private Queue<RoomParticipant> participantQueue;
*/
private Queue<RoomParticipant> participantQueue; private boolean archivingRunning = false;
private boolean archivingRunning = false; private TimerTask archiveTask;
private TimerTask cleanupTask;
private TimerTask archiveTask;
private TimerTask cleanupTask; private TimerTask maxAgeTask;
private Collection<ConversationListener> conversationListeners; private Collection<ConversationListener> conversationListeners;
/** /**
* Keeps the address of those components that provide the gateway service. * Keeps the address of those components that provide the gateway service.
*/ */
private List<String> gateways; private List<String> gateways;
private XMPPServerInfo serverInfo; private XMPPServerInfo serverInfo;
public ConversationManager(TaskEngine taskEngine) { public ConversationManager(TaskEngine taskEngine) {
this.taskEngine = taskEngine; this.taskEngine = taskEngine;
this.gateways = new CopyOnWriteArrayList<String>(); this.gateways = new CopyOnWriteArrayList<String>();
this.serverInfo = XMPPServer.getInstance().getServerInfo(); this.serverInfo = XMPPServer.getInstance().getServerInfo();
this.conversationEventsQueue = new ConversationEventsQueue(this, taskEngine); this.conversationEventsQueue = new ConversationEventsQueue(this, taskEngine);
} }
public void start() { public void start() {
metadataArchivingEnabled = JiveGlobals.getBooleanProperty("conversation.metadataArchiving", true); metadataArchivingEnabled = JiveGlobals.getBooleanProperty("conversation.metadataArchiving", true);
messageArchivingEnabled = JiveGlobals.getBooleanProperty("conversation.messageArchiving", false); messageArchivingEnabled = JiveGlobals.getBooleanProperty("conversation.messageArchiving", false);
if (messageArchivingEnabled && !metadataArchivingEnabled) { if (messageArchivingEnabled && !metadataArchivingEnabled) {
Log.warn("Metadata archiving must be enabled when message archiving is enabled. Overriding setting."); Log.warn("Metadata archiving must be enabled when message archiving is enabled. Overriding setting.");
metadataArchivingEnabled = true; metadataArchivingEnabled = true;
} }
roomArchivingEnabled = JiveGlobals.getBooleanProperty("conversation.roomArchiving", false); roomArchivingEnabled = JiveGlobals.getBooleanProperty("conversation.roomArchiving", false);
roomsArchived = StringUtils.stringToCollection(JiveGlobals.getProperty("conversation.roomsArchived", "")); roomsArchived = StringUtils.stringToCollection(JiveGlobals.getProperty("conversation.roomsArchived", ""));
if (roomArchivingEnabled && !metadataArchivingEnabled) { if (roomArchivingEnabled && !metadataArchivingEnabled) {
Log.warn("Metadata archiving must be enabled when room archiving is enabled. Overriding setting."); Log.warn("Metadata archiving must be enabled when room archiving is enabled. Overriding setting.");
metadataArchivingEnabled = true; metadataArchivingEnabled = true;
} }
idleTime = JiveGlobals.getIntProperty("conversation.idleTime", DEFAULT_IDLE_TIME) * idleTime = JiveGlobals.getIntProperty("conversation.idleTime", DEFAULT_IDLE_TIME) * JiveConstants.MINUTE;
JiveConstants.MINUTE; maxTime = JiveGlobals.getIntProperty("conversation.maxTime", DEFAULT_MAX_TIME) * JiveConstants.MINUTE;
maxTime = JiveGlobals.getIntProperty("conversation.maxTime",
DEFAULT_MAX_TIME) * JiveConstants.MINUTE; maxAge = JiveGlobals.getIntProperty("conversation.maxAge", DEFAULT_MAX_AGE) * JiveConstants.DAY;
// Listen for any changes to the conversation properties. maxRetrievable = JiveGlobals.getIntProperty("conversation.maxRetrievable", DEFAULT_MAX_RETRIEVABLE) * JiveConstants.DAY;
propertyListener = new ConversationPropertyListener();
PropertyEventDispatcher.addListener(propertyListener); // Listen for any changes to the conversation properties.
propertyListener = new ConversationPropertyListener();
conversationQueue = new ConcurrentLinkedQueue<Conversation>(); PropertyEventDispatcher.addListener(propertyListener);
messageQueue = new ConcurrentLinkedQueue<ArchivedMessage>();
participantQueue = new ConcurrentLinkedQueue<RoomParticipant>(); conversationQueue = new ConcurrentLinkedQueue<Conversation>();
messageQueue = new ConcurrentLinkedQueue<ArchivedMessage>();
conversationListeners = new CopyOnWriteArraySet<ConversationListener>(); participantQueue = new ConcurrentLinkedQueue<RoomParticipant>();
// Schedule a task to do conversation archiving. conversationListeners = new CopyOnWriteArraySet<ConversationListener>();
archiveTask = new TimerTask() {
@Override // Schedule a task to do conversation archiving.
archiveTask = new TimerTask() {
@Override
public void run() {
new ArchivingTask().run();
}
};
taskEngine.scheduleAtFixedRate(archiveTask, JiveConstants.MINUTE, JiveConstants.MINUTE);
// Schedule a task to do conversation cleanup.
cleanupTask = new TimerTask() {
@Override
public void run() { public void run() {
new ArchivingTask().run(); for (String key : conversations.keySet()) {
} Conversation conversation = conversations.get(key);
}; long now = System.currentTimeMillis();
taskEngine.scheduleAtFixedRate(archiveTask, JiveConstants.MINUTE, JiveConstants.MINUTE); if ((now - conversation.getLastActivity().getTime() > idleTime) || (now - conversation.getStartDate().getTime() > maxTime)) {
removeConversation(key, conversation, new Date(now));
// Schedule a task to do conversation cleanup. }
cleanupTask = new TimerTask() { }
@Override }
};
taskEngine.scheduleAtFixedRate(cleanupTask, JiveConstants.MINUTE * 5, JiveConstants.MINUTE * 5);
// Schedule a task to do conversation purging.
maxAgeTask = new TimerTask() {
@Override
public void run() { public void run() {
for (String key : conversations.keySet()) { // Delete conversations older than maxAge days
Conversation conversation = conversations.get(key); // TODO
long now = System.currentTimeMillis(); }
if ((now - conversation.getLastActivity().getTime() > idleTime) || };
(now - conversation.getStartDate().getTime() > maxTime)) { taskEngine.scheduleAtFixedRate(maxAgeTask, JiveConstants.MINUTE, JiveConstants.DAY);
removeConversation(key, conversation, new Date(now));
} // Register a statistic.
} Statistic conversationStat = new Statistic() {
}
}; public String getName() {
taskEngine.scheduleAtFixedRate(cleanupTask, JiveConstants.MINUTE * 5, JiveConstants.MINUTE * 5); return LocaleUtils.getLocalizedString("stat.conversation.name", MonitoringConstants.NAME);
}
// Register a statistic.
Statistic conversationStat = new Statistic() { public Type getStatType() {
return Type.count;
public String getName() { }
return LocaleUtils.getLocalizedString("stat.conversation.name", MonitoringConstants.NAME);
} public String getDescription() {
return LocaleUtils.getLocalizedString("stat.conversation.desc", MonitoringConstants.NAME);
public Type getStatType() { }
return Type.count;
} public String getUnits() {
return LocaleUtils.getLocalizedString("stat.conversation.units", MonitoringConstants.NAME);
public String getDescription() { }
return LocaleUtils.getLocalizedString("stat.conversation.desc", MonitoringConstants.NAME);
} public double sample() {
return getConversationCount();
public String getUnits() { }
return LocaleUtils.getLocalizedString("stat.conversation.units", MonitoringConstants.NAME);
} public boolean isPartialSample() {
return false;
public double sample() { }
return getConversationCount(); };
} StatisticsManager.getInstance().addStatistic(CONVERSATIONS_KEY, conversationStat);
InternalComponentManager.getInstance().addListener(this);
public boolean isPartialSample() { }
return false;
} public void stop() {
}; archiveTask.cancel();
StatisticsManager.getInstance().addStatistic(CONVERSATIONS_KEY, conversationStat); archiveTask = null;
InternalComponentManager.getInstance().addListener(this); cleanupTask.cancel();
} cleanupTask = null;
public void stop() { // Remove the statistics.
archiveTask.cancel(); StatisticsManager.getInstance().removeStatistic(CONVERSATIONS_KEY);
archiveTask = null;
cleanupTask.cancel(); PropertyEventDispatcher.removeListener(propertyListener);
cleanupTask = null; propertyListener = null;
conversations.clear();
// Remove the statistics. conversations = null;
StatisticsManager.getInstance().removeStatistic(CONVERSATIONS_KEY);
// Archive anything remaining in the queue before quitting.
PropertyEventDispatcher.removeListener(propertyListener); new ArchivingTask().run();
propertyListener = null;
conversations.clear(); conversationQueue.clear();
conversations = null; conversationQueue = null;
// Archive anything remaining in the queue before quitting. messageQueue.clear();
new ArchivingTask().run(); messageQueue = null;
conversationQueue.clear(); conversationListeners.clear();
conversationQueue = null; conversationListeners = null;
messageQueue.clear(); serverInfo = null;
messageQueue = null; InternalComponentManager.getInstance().removeListener(this);
}
conversationListeners.clear();
conversationListeners = null; /**
* Returns true if metadata archiving is enabled. Conversation meta-data includes the participants, start date, last activity, and the count of
serverInfo = null; * messages sent. When archiving is enabled, all meta-data is written to the database.
InternalComponentManager.getInstance().removeListener(this); *
} * @return true if metadata archiving is enabled.
*/
/** public boolean isMetadataArchivingEnabled() {
* Returns true if metadata archiving is enabled. Conversation meta-data includes return metadataArchivingEnabled;
* the participants, start date, last activity, and the count of messages sent. }
* When archiving is enabled, all meta-data is written to the database.
* /**
* @return true if metadata archiving is enabled. * Sets whether metadata archiving is enabled. Conversation meta-data includes the participants, start date, last activity, and the count of
*/ * messages sent. When archiving is enabled, all meta-data is written to the database.
public boolean isMetadataArchivingEnabled() { *
return metadataArchivingEnabled; * @param enabled
} * true if archiving should be enabled.
*/
/** public void setMetadataArchivingEnabled(boolean enabled) {
* Sets whether metadata archiving is enabled. Conversation meta-data includes this.metadataArchivingEnabled = enabled;
* the participants, start date, last activity, and the count of messages sent. JiveGlobals.setProperty("conversation.metadataArchiving", Boolean.toString(enabled));
* When archiving is enabled, all meta-data is written to the database. }
*
* @param enabled true if archiving should be enabled. /**
*/ * Returns true if one-to-one chats or group chats messages are being archived.
public void setMetadataArchivingEnabled(boolean enabled) { *
this.metadataArchivingEnabled = enabled; * @return true if one-to-one chats or group chats messages are being archived.
JiveGlobals.setProperty("conversation.metadataArchiving", Boolean.toString(enabled)); */
} public boolean isArchivingEnabled() {
return isMessageArchivingEnabled() || isRoomArchivingEnabled();
/** }
* Returns true if one-to-one chats or group chats messages are being archived.
* /**
* @return true if one-to-one chats or group chats messages are being archived. * Returns true if message archiving is enabled for one-to-one chats. When enabled, all messages in one-to-one conversations are stored in the
*/ * database. Note: it's not possible for meta-data archiving to be disabled when message archiving is enabled; enabling message archiving
public boolean isArchivingEnabled() { * automatically enables meta-data archiving.
return isMessageArchivingEnabled() || isRoomArchivingEnabled(); *
} * @return true if message archiving is enabled.
*/
/** public boolean isMessageArchivingEnabled() {
* Returns true if message archiving is enabled for one-to-one chats. When enabled, all messages return messageArchivingEnabled;
* in one-to-one conversations are stored in the database. Note: it's not possible for }
* meta-data archiving to be disabled when message archiving is enabled; enabling
* message archiving automatically enables meta-data archiving. /**
* * Sets whether message archiving is enabled. When enabled, all messages in conversations are stored in the database. Note: it's not possible for
* @return true if message archiving is enabled. * meta-data archiving to be disabled when message archiving is enabled; enabling message archiving automatically enables meta-data archiving.
*/ *
public boolean isMessageArchivingEnabled() { * @param enabled
return messageArchivingEnabled; * true if message should be enabled.
} */
public void setMessageArchivingEnabled(boolean enabled) {
/** this.messageArchivingEnabled = enabled;
* Sets whether message archiving is enabled. When enabled, all messages JiveGlobals.setProperty("conversation.messageArchiving", Boolean.toString(enabled));
* in conversations are stored in the database. Note: it's not possible for // Force metadata archiving enabled.
* meta-data archiving to be disabled when message archiving is enabled; enabling if (enabled) {
* message archiving automatically enables meta-data archiving. this.metadataArchivingEnabled = true;
* }
* @param enabled true if message should be enabled. }
*/
public void setMessageArchivingEnabled(boolean enabled) { /**
this.messageArchivingEnabled = enabled; * Returns true if message archiving is enabled for group chats. When enabled, all messages in group conversations are stored in the database
JiveGlobals.setProperty("conversation.messageArchiving", Boolean.toString(enabled)); * unless a list of rooms was specified in {@link #getRoomsArchived()} . Note: it's not possible for meta-data archiving to be disabled when room
// Force metadata archiving enabled. * archiving is enabled; enabling room archiving automatically enables meta-data archiving.
if (enabled) { *
this.metadataArchivingEnabled = true; * @return true if room archiving is enabled.
} */
} public boolean isRoomArchivingEnabled() {
return roomArchivingEnabled;
/** }
* Returns true if message archiving is enabled for group chats. When enabled, all messages
* in group conversations are stored in the database unless a list of rooms was specified /**
* in {@link #getRoomsArchived()} . Note: it's not possible for meta-data archiving to be * Sets whether message archiving is enabled for group chats. When enabled, all messages in group conversations are stored in the database unless
* disabled when room archiving is enabled; enabling room archiving automatically * a list of rooms was specified in {@link #getRoomsArchived()} . Note: it's not possible for meta-data archiving to be disabled when room
* enables meta-data archiving. * archiving is enabled; enabling room archiving automatically enables meta-data archiving.
* *
* @return true if room archiving is enabled. * @param enabled
*/ * if room archiving is enabled.
public boolean isRoomArchivingEnabled() { */
return roomArchivingEnabled; public void setRoomArchivingEnabled(boolean enabled) {
} this.roomArchivingEnabled = enabled;
JiveGlobals.setProperty("conversation.roomArchiving", Boolean.toString(enabled));
/** // Force metadata archiving enabled.
* Sets whether message archiving is enabled for group chats. When enabled, all messages if (enabled) {
* in group conversations are stored in the database unless a list of rooms was specified this.metadataArchivingEnabled = true;
* in {@link #getRoomsArchived()} . Note: it's not possible for meta-data archiving to be }
* disabled when room archiving is enabled; enabling room archiving automatically }
* enables meta-data archiving.
* /**
* @param enabled if room archiving is enabled. * Returns list of room names whose messages will be archived. When room archiving is enabled and this list is empty then messages of all local
*/ * rooms will be archived. However, when name of rooms are defined in this list then only messages of those rooms will be archived.
public void setRoomArchivingEnabled(boolean enabled) { *
this.roomArchivingEnabled = enabled; * @return list of local room names whose messages will be archived.
JiveGlobals.setProperty("conversation.roomArchiving", Boolean.toString(enabled)); */
// Force metadata archiving enabled. public Collection<String> getRoomsArchived() {
if (enabled) { return roomsArchived;
this.metadataArchivingEnabled = true; }
}
} /**
* Sets list of room names whose messages will be archived. When room archiving is enabled and this list is empty then messages of all local rooms
/** * will be archived. However, when name of rooms are defined in this list then only messages of those rooms will be archived.
* Returns list of room names whose messages will be archived. When room archiving is enabled and *
* this list is empty then messages of all local rooms will be archived. However, when name of * @param roomsArchived
* rooms are defined in this list then only messages of those rooms will be archived. * list of local room names whose messages will be archived.
* */
* @return list of local room names whose messages will be archived. public void setRoomsArchived(Collection<String> roomsArchived) {
*/ this.roomsArchived = roomsArchived;
public Collection<String> getRoomsArchived() { JiveGlobals.setProperty("conversation.roomsArchived", StringUtils.collectionToString(roomsArchived));
return roomsArchived; }
}
/**
/** * Returns the number of minutes a conversation can be idle before it's ended.
* Sets list of room names whose messages will be archived. When room archiving is enabled and *
* this list is empty then messages of all local rooms will be archived. However, when name of * @return the conversation idle time.
* rooms are defined in this list then only messages of those rooms will be archived. */
* public int getIdleTime() {
* @param roomsArchived list of local room names whose messages will be archived. return (int) (idleTime / JiveConstants.MINUTE);
*/ }
public void setRoomsArchived(Collection<String> roomsArchived) {
this.roomsArchived = roomsArchived; /**
JiveGlobals.setProperty("conversation.roomsArchived", StringUtils.collectionToString(roomsArchived)); * Sets the number of minutes a conversation can be idle before it's ended.
} *
* @param idleTime
/** * the max number of minutes a conversation can be idle before it's ended.
* Returns the number of minutes a conversation can be idle before it's ended. * @throws IllegalArgumentException
* * if idleTime is less than 1.
* @return the conversation idle time. */
*/ public void setIdleTime(int idleTime) {
public int getIdleTime() { if (idleTime < 1) {
return (int)(idleTime / JiveConstants.MINUTE); throw new IllegalArgumentException("Idle time less than 1 is not valid: " + idleTime);
} }
JiveGlobals.setProperty("conversation.idleTime", Integer.toString(idleTime));
/** this.idleTime = idleTime * JiveConstants.MINUTE;
* Sets the number of minutes a conversation can be idle before it's ended. }
*
* @param idleTime the max number of minutes a conversation can be idle before it's ended. /**
* @throws IllegalArgumentException if idleTime is less than 1. * Returns the maximum number of minutes a conversation can last before it's ended. Any additional messages between the participants in the chat
*/ * will be associated with a new conversation.
public void setIdleTime(int idleTime) { *
if (idleTime < 1) { * @return the maximum number of minutes a conversation can last.
throw new IllegalArgumentException("Idle time less than 1 is not valid: " + idleTime); */
} public int getMaxTime() {
JiveGlobals.setProperty("conversation.idleTime", Integer.toString(idleTime)); return (int) (maxTime / JiveConstants.MINUTE);
this.idleTime = idleTime * JiveConstants.MINUTE; }
}
/**
/** * Sets the maximum number of minutes a conversation can last before it's ended. Any additional messages between the participants in the chat will
* Returns the maximum number of minutes a conversation can last before it's ended. * be associated with a new conversation.
* Any additional messages between the participants in the chat will be associated *
* with a new conversation. * @param maxTime
* * the maximum number of minutes a conversation can last.
* @return the maximum number of minutes a conversation can last. * @throws IllegalArgumentException
*/ * if maxTime is less than 1.
public int getMaxTime() { */
return (int)(maxTime / JiveConstants.MINUTE); public void setMaxTime(int maxTime) {
} if (maxTime < 1) {
throw new IllegalArgumentException("Max time less than 1 is not valid: " + maxTime);
/** }
* Sets the maximum number of minutes a conversation can last before it's ended. JiveGlobals.setProperty("conversation.maxTime", Integer.toString(maxTime));
* Any additional messages between the participants in the chat will be associated this.maxTime = maxTime * JiveConstants.MINUTE;
* with a new conversation. }
*
* @param maxTime the maximum number of minutes a conversation can last. public int getMaxAge() {
* @throws IllegalArgumentException if maxTime is less than 1. return (int) (maxAge / JiveConstants.DAY);
*/ }
public void setMaxTime(int maxTime) {
if (maxTime < 1) { public void setMaxAge(int maxAge) {
throw new IllegalArgumentException("Max time less than 1 is not valid: " + maxTime); if (maxAge < 0) {
} throw new IllegalArgumentException("Max age less than 0 is not valid: " + maxAge);
JiveGlobals.setProperty("conversation.maxTime", Integer.toString(maxTime)); }
this.maxTime = maxTime * JiveConstants.MINUTE; JiveGlobals.setProperty("conversation.maxAge", Integer.toString(maxAge));
} this.maxAge = maxAge * JiveConstants.DAY;
}
public ConversationEventsQueue getConversationEventsQueue() {
return conversationEventsQueue; public int getMaxRetrievable() {
} return (int) (maxRetrievable / JiveConstants.DAY);
}
/**
* Returns the count of active conversations. public void setMaxRetrievable(int maxRetrievable) {
* if (maxRetrievable < 0) {
* @return the count of active conversations. throw new IllegalArgumentException("Max retrievable less than 0 is not valid: " + maxRetrievable);
*/ }
public int getConversationCount() { JiveGlobals.setProperty("conversation.maxRetrievable", Integer.toString(maxRetrievable));
if (ClusterManager.isSeniorClusterMember()) { this.maxRetrievable = maxRetrievable * JiveConstants.DAY;
return conversations.size(); }
}
return (Integer) CacheFactory.doSynchronousClusterTask(new GetConversationCountTask(), public ConversationEventsQueue getConversationEventsQueue() {
ClusterManager.getSeniorClusterMember().toByteArray()); return conversationEventsQueue;
} }
/** /**
* Returns a conversation by ID. * Returns the count of active conversations.
* *
* @param conversationID the ID of the conversation. * @return the count of active conversations.
* @return the conversation. */
* @throws NotFoundException if the conversation could not be found. public int getConversationCount() {
*/ if (ClusterManager.isSeniorClusterMember()) {
public Conversation getConversation(long conversationID) throws NotFoundException { return conversations.size();
if (ClusterManager.isSeniorClusterMember()) { }
// Search through the currently active conversations. return (Integer) CacheFactory.doSynchronousClusterTask(new GetConversationCountTask(), ClusterManager.getSeniorClusterMember().toByteArray());
for (Conversation conversation : conversations.values()) { }
if (conversation.getConversationID() == conversationID) {
return conversation; /**
} * Returns a conversation by ID.
} *
// Otherwise, it might be an archived conversation, so attempt to load it. * @param conversationID
return new Conversation(this, conversationID); * the ID of the conversation.
} * @return the conversation.
else { * @throws NotFoundException
// Get this info from the senior cluster member when running in a cluster * if the conversation could not be found.
Conversation conversation = (Conversation) CacheFactory.doSynchronousClusterTask( */
new GetConversationTask(conversationID), ClusterManager.getSeniorClusterMember().toByteArray()); public Conversation getConversation(long conversationID) throws NotFoundException {
if (conversation == null) { if (ClusterManager.isSeniorClusterMember()) {
throw new NotFoundException("Conversation not found: " + conversationID); // Search through the currently active conversations.
} for (Conversation conversation : conversations.values()) {
return conversation; if (conversation.getConversationID() == conversationID) {
} return conversation;
} }
}
/** // Otherwise, it might be an archived conversation, so attempt to load it.
* Returns the set of active conversations. return new Conversation(this, conversationID);
* } else {
* @return the active conversations. // Get this info from the senior cluster member when running in a cluster
*/ Conversation conversation = (Conversation) CacheFactory.doSynchronousClusterTask(new GetConversationTask(conversationID), ClusterManager
public Collection<Conversation> getConversations() { .getSeniorClusterMember().toByteArray());
if (ClusterManager.isSeniorClusterMember()) { if (conversation == null) {
List<Conversation> conversationList = new ArrayList<Conversation>(conversations.values()); throw new NotFoundException("Conversation not found: " + conversationID);
// Sort the conversations by creation date. }
Collections.sort(conversationList, new Comparator<Conversation>() { return conversation;
public int compare(Conversation c1, Conversation c2) { }
return c1.getStartDate().compareTo(c2.getStartDate()); }
}
}); /**
return conversationList; * Returns the set of active conversations.
} *
else { * @return the active conversations.
// Get this info from the senior cluster member when running in a cluster */
return (Collection<Conversation>) CacheFactory.doSynchronousClusterTask(new GetConversationsTask(), public Collection<Conversation> getConversations() {
ClusterManager.getSeniorClusterMember().toByteArray()); if (ClusterManager.isSeniorClusterMember()) {
} List<Conversation> conversationList = new ArrayList<Conversation>(conversations.values());
} // Sort the conversations by creation date.
Collections.sort(conversationList, new Comparator<Conversation>() {
/** public int compare(Conversation c1, Conversation c2) {
* Returns the total number of conversations that have been archived to the database. return c1.getStartDate().compareTo(c2.getStartDate());
* The archived conversation may only be the meta-data, or it might include messages }
* as well if message archiving is turned on. });
* return conversationList;
* @return the total number of archived conversations. } else {
*/ // Get this info from the senior cluster member when running in a cluster
public int getArchivedConversationCount() { return (Collection<Conversation>) CacheFactory.doSynchronousClusterTask(new GetConversationsTask(), ClusterManager
int conversationCount = 0; .getSeniorClusterMember().toByteArray());
Connection con = null; }
PreparedStatement pstmt = null; }
ResultSet rs = null;
try { /**
con = DbConnectionManager.getConnection(); * Returns the total number of conversations that have been archived to the database. The archived conversation may only be the meta-data, or it
pstmt = con.prepareStatement(CONVERSATION_COUNT); * might include messages as well if message archiving is turned on.
rs = pstmt.executeQuery(); *
if (rs.next()) { * @return the total number of archived conversations.
conversationCount = rs.getInt(1); */
} public int getArchivedConversationCount() {
} int conversationCount = 0;
catch (SQLException sqle) { Connection con = null;
Log.error(sqle.getMessage(), sqle); PreparedStatement pstmt = null;
} ResultSet rs = null;
finally { try {
DbConnectionManager.closeConnection(rs, pstmt, con); con = DbConnectionManager.getConnection();
} pstmt = con.prepareStatement(CONVERSATION_COUNT);
return conversationCount; rs = pstmt.executeQuery();
} if (rs.next()) {
conversationCount = rs.getInt(1);
/** }
* Returns the total number of messages that have been archived to the database. } catch (SQLException sqle) {
* Log.error(sqle.getMessage(), sqle);
* @return the total number of archived messages. } finally {
*/ DbConnectionManager.closeConnection(rs, pstmt, con);
public int getArchivedMessageCount() { }
int messageCount = 0; return conversationCount;
Connection con = null; }
PreparedStatement pstmt = null;
ResultSet rs = null; /**
try { * Returns the total number of messages that have been archived to the database.
con = DbConnectionManager.getConnection(); *
pstmt = con.prepareStatement(MESSAGE_COUNT); * @return the total number of archived messages.
rs = pstmt.executeQuery(); */
if (rs.next()) { public int getArchivedMessageCount() {
messageCount = rs.getInt(1); int messageCount = 0;
} Connection con = null;
} PreparedStatement pstmt = null;
catch (SQLException sqle) { ResultSet rs = null;
Log.error(sqle.getMessage(), sqle); try {
} con = DbConnectionManager.getConnection();
finally { pstmt = con.prepareStatement(MESSAGE_COUNT);
DbConnectionManager.closeConnection(rs, pstmt, con); rs = pstmt.executeQuery();
} if (rs.next()) {
return messageCount; messageCount = rs.getInt(1);
} }
} catch (SQLException sqle) {
/** Log.error(sqle.getMessage(), sqle);
* Adds a conversation listener, which will be notified of newly created conversations, } finally {
* conversations ending, and updates to conversations. DbConnectionManager.closeConnection(rs, pstmt, con);
* }
* @param listener the conversation listener. return messageCount;
*/ }
public void addConversationListener(ConversationListener listener) {
conversationListeners.add(listener); /**
} * Adds a conversation listener, which will be notified of newly created conversations, conversations ending, and updates to conversations.
*
/** * @param listener
* Removes a conversation listener. * the conversation listener.
* */
* @param listener the conversation listener. public void addConversationListener(ConversationListener listener) {
*/ conversationListeners.add(listener);
public void removeConversationListener(ConversationListener listener) { }
conversationListeners.remove(listener);
} /**
* Removes a conversation listener.
/** *
* Processes an incoming message of a one-to-one chat. The message will mapped to a * @param listener
* conversation and then queued for storage if archiving is turned on. * the conversation listener.
* */
* @param sender sender of the message. public void removeConversationListener(ConversationListener listener) {
* @param receiver receiver of the message. conversationListeners.remove(listener);
* @param body body of the message. }
* @param date date when the message was sent.
*/ /**
void processMessage(JID sender, JID receiver, String body, Date date) { * Processes an incoming message of a one-to-one chat. The message will mapped to a conversation and then queued for storage if archiving is
String conversationKey = getConversationKey(sender, receiver); * turned on.
synchronized (conversationKey.intern()) { *
Conversation conversation = conversations.get(conversationKey); * @param sender
// Create a new conversation if necessary. * sender of the message.
if (conversation == null) { * @param receiver
Collection<JID> participants = new ArrayList<JID>(2); * receiver of the message.
participants.add(sender); * @param body
participants.add(receiver); * body of the message.
XMPPServer server = XMPPServer.getInstance(); * @param date
// Check to see if this is an external conversation; i.e. one of the participants * date when the message was sent.
// is on a different server. We can use XOR since we know that both JID's can't */
// be external. void processMessage(JID sender, JID receiver, String body, Date date) {
boolean external = isExternal(server, sender) ^ isExternal(server, receiver); String conversationKey = getConversationKey(sender, receiver);
// Make sure that the user joined the conversation before a message was received synchronized (conversationKey.intern()) {
Date start = new Date(date.getTime() - 1); Conversation conversation = conversations.get(conversationKey);
conversation = new Conversation(this, participants, external, start); // Create a new conversation if necessary.
conversations.put(conversationKey, conversation); if (conversation == null) {
// Notify listeners of the newly created conversation. Collection<JID> participants = new ArrayList<JID>(2);
for (ConversationListener listener : conversationListeners) { participants.add(sender);
listener.conversationCreated(conversation); participants.add(receiver);
} XMPPServer server = XMPPServer.getInstance();
} // Check to see if this is an external conversation; i.e. one of the participants
// Check to see if the current conversation exceeds either the max idle time // is on a different server. We can use XOR since we know that both JID's can't
// or max conversation time. // be external.
else if ((date.getTime() - conversation.getLastActivity().getTime() > idleTime) || boolean external = isExternal(server, sender) ^ isExternal(server, receiver);
(date.getTime() - conversation.getStartDate().getTime() > maxTime)) { // Make sure that the user joined the conversation before a message was received
removeConversation(conversationKey, conversation, conversation.getLastActivity()); Date start = new Date(date.getTime() - 1);
conversation = new Conversation(this, participants, external, start);
Collection<JID> participants = new ArrayList<JID>(2); conversations.put(conversationKey, conversation);
participants.add(sender); // Notify listeners of the newly created conversation.
participants.add(receiver); for (ConversationListener listener : conversationListeners) {
XMPPServer server = XMPPServer.getInstance(); listener.conversationCreated(conversation);
// Check to see if this is an external conversation; i.e. one of the participants }
// is on a different server. We can use XOR since we know that both JID's can't }
// be external. // Check to see if the current conversation exceeds either the max idle time
boolean external = isExternal(server, sender) ^ isExternal(server, receiver); // or max conversation time.
// Make sure that the user joined the conversation before a message was received else if ((date.getTime() - conversation.getLastActivity().getTime() > idleTime)
Date start = new Date(date.getTime() - 1); || (date.getTime() - conversation.getStartDate().getTime() > maxTime)) {
conversation = new Conversation(this, participants, external, start); removeConversation(conversationKey, conversation, conversation.getLastActivity());
conversations.put(conversationKey, conversation);
// Notify listeners of the newly created conversation. Collection<JID> participants = new ArrayList<JID>(2);
for (ConversationListener listener : conversationListeners) { participants.add(sender);
listener.conversationCreated(conversation); participants.add(receiver);
} XMPPServer server = XMPPServer.getInstance();
} // Check to see if this is an external conversation; i.e. one of the participants
// Record the newly received message. // is on a different server. We can use XOR since we know that both JID's can't
conversation.messageReceived(sender, date); // be external.
if (metadataArchivingEnabled) { boolean external = isExternal(server, sender) ^ isExternal(server, receiver);
conversationQueue.add(conversation); // Make sure that the user joined the conversation before a message was received
} Date start = new Date(date.getTime() - 1);
if (messageArchivingEnabled) { conversation = new Conversation(this, participants, external, start);
messageQueue conversations.put(conversationKey, conversation);
.add(new ArchivedMessage(conversation.getConversationID(), sender, receiver, date, body, false)); // Notify listeners of the newly created conversation.
} for (ConversationListener listener : conversationListeners) {
// Notify listeners of the conversation update. listener.conversationCreated(conversation);
for (ConversationListener listener : conversationListeners) { }
listener.conversationUpdated(conversation, date); }
} // Record the newly received message.
} conversation.messageReceived(sender, date);
} if (metadataArchivingEnabled) {
conversationQueue.add(conversation);
/** }
* Processes an incoming message sent to a room. The message will mapped to a conversation and then if (messageArchivingEnabled) {
* queued for storage if archiving is turned on. messageQueue.add(new ArchivedMessage(conversation.getConversationID(), sender, receiver, date, body, false));
* }
* @param roomJID the JID of the room where the group conversation is taking place. // Notify listeners of the conversation update.
* @param sender the JID of the entity that sent the message. for (ConversationListener listener : conversationListeners) {
* @param nickname nickname of the user in the room when the message was sent. listener.conversationUpdated(conversation, date);
* @param body the message sent to the room. }
* @param date date when the message was sent. }
*/ }
void processRoomMessage(JID roomJID, JID sender, String nickname, String body, Date date) {
String conversationKey = getRoomConversationKey(roomJID); /**
synchronized (conversationKey.intern()) { * Processes an incoming message sent to a room. The message will mapped to a conversation and then queued for storage if archiving is turned on.
Conversation conversation = conversations.get(conversationKey); *
// Create a new conversation if necessary. * @param roomJID
if (conversation == null) { * the JID of the room where the group conversation is taking place.
// Make sure that the user joined the conversation before a message was received * @param sender
Date start = new Date(date.getTime() - 1); * the JID of the entity that sent the message.
conversation = new Conversation(this, roomJID, false, start); * @param nickname
conversations.put(conversationKey, conversation); * nickname of the user in the room when the message was sent.
// Notify listeners of the newly created conversation. * @param body
for (ConversationListener listener : conversationListeners) { * the message sent to the room.
listener.conversationCreated(conversation); * @param date
} * date when the message was sent.
} */
// Check to see if the current conversation exceeds either the max idle time void processRoomMessage(JID roomJID, JID sender, String nickname, String body, Date date) {
// or max conversation time. String conversationKey = getRoomConversationKey(roomJID);
else if ((date.getTime() - conversation.getLastActivity().getTime() > idleTime) || synchronized (conversationKey.intern()) {
(date.getTime() - conversation.getStartDate().getTime() > maxTime)) { Conversation conversation = conversations.get(conversationKey);
removeConversation(conversationKey, conversation, conversation.getLastActivity()); // Create a new conversation if necessary.
// Make sure that the user joined the conversation before a message was received if (conversation == null) {
Date start = new Date(date.getTime() - 1); // Make sure that the user joined the conversation before a message was received
conversation = new Conversation(this, roomJID, false, start); Date start = new Date(date.getTime() - 1);
conversations.put(conversationKey, conversation); conversation = new Conversation(this, roomJID, false, start);
// Notify listeners of the newly created conversation. conversations.put(conversationKey, conversation);
for (ConversationListener listener : conversationListeners) { // Notify listeners of the newly created conversation.
listener.conversationCreated(conversation); for (ConversationListener listener : conversationListeners) {
} listener.conversationCreated(conversation);
} }
// Record the newly received message. }
conversation.messageReceived(sender, date); // Check to see if the current conversation exceeds either the max idle time
if (metadataArchivingEnabled) { // or max conversation time.
conversationQueue.add(conversation); else if ((date.getTime() - conversation.getLastActivity().getTime() > idleTime)
} || (date.getTime() - conversation.getStartDate().getTime() > maxTime)) {
if (roomArchivingEnabled && (roomsArchived.isEmpty() || roomsArchived.contains(roomJID.getNode()))) { removeConversation(conversationKey, conversation, conversation.getLastActivity());
JID jid = new JID(roomJID + "/" + nickname); // Make sure that the user joined the conversation before a message was received
messageQueue.add(new ArchivedMessage(conversation.getConversationID(), sender, jid, date, body, false)); Date start = new Date(date.getTime() - 1);
} conversation = new Conversation(this, roomJID, false, start);
// Notify listeners of the conversation update. conversations.put(conversationKey, conversation);
for (ConversationListener listener : conversationListeners) { // Notify listeners of the newly created conversation.
listener.conversationUpdated(conversation, date); for (ConversationListener listener : conversationListeners) {
} listener.conversationCreated(conversation);
} }
} }
// Record the newly received message.
/** conversation.messageReceived(sender, date);
* Notification message indicating that a user joined a groupchat conversation. If if (metadataArchivingEnabled) {
* no groupchat conversation was taking place in the specified room then ignore this conversationQueue.add(conversation);
* event.<p> }
* <p/> if (roomArchivingEnabled && (roomsArchived.isEmpty() || roomsArchived.contains(roomJID.getNode()))) {
* Eventually, when a new conversation will start in the room and if this user is JID jid = new JID(roomJID + "/" + nickname);
* still in the room then the new conversation will detect this user and mark like messageQueue.add(new ArchivedMessage(conversation.getConversationID(), sender, jid, date, body, false));
* if the user joined the converstion from the beginning. }
* // Notify listeners of the conversation update.
* @param room the room where the user joined. for (ConversationListener listener : conversationListeners) {
* @param user the user that joined the room. listener.conversationUpdated(conversation, date);
* @param nickname nickname of the user in the room. }
* @param date date when the user joined the group coversation. }
*/ }
void joinedGroupConversation(JID room, JID user, String nickname, Date date) {
Conversation conversation = getRoomConversation(room); /**
if (conversation != null) { * Notification message indicating that a user joined a groupchat conversation. If no groupchat conversation was taking place in the specified
conversation.participantJoined(user, nickname, date.getTime()); * room then ignore this event.
} * <p>
} * <p/>
* Eventually, when a new conversation will start in the room and if this user is still in the room then the new conversation will detect this
/** * user and mark like if the user joined the converstion from the beginning.
* Notification message indicating that a user left a groupchat conversation. If *
* no groupchat conversation was taking place in the specified room then ignore this * @param room
* event. * the room where the user joined.
* * @param user
* @param room the room where the user left. * the user that joined the room.
* @param user the user that left the room. * @param nickname
* @param date date when the user left the group coversation. * nickname of the user in the room.
*/ * @param date
void leftGroupConversation(JID room, JID user, Date date) { * date when the user joined the group coversation.
Conversation conversation = getRoomConversation(room); */
if (conversation != null) { void joinedGroupConversation(JID room, JID user, String nickname, Date date) {
conversation.participantLeft(user, date.getTime()); Conversation conversation = getRoomConversation(room);
} if (conversation != null) {
} conversation.participantJoined(user, nickname, date.getTime());
}
void roomConversationEnded(JID room, Date date) { }
Conversation conversation = getRoomConversation(room);
if (conversation != null) { /**
removeConversation(room.toString(), conversation, date); * Notification message indicating that a user left a groupchat conversation. If no groupchat conversation was taking place in the specified room
} * then ignore this event.
} *
* @param room
private void removeConversation(String key, Conversation conversation, Date date) { * the room where the user left.
conversations.remove(key); * @param user
// Notify conversation that it has ended * the user that left the room.
conversation.conversationEnded(date); * @param date
// Notify listeners of the conversation ending. * date when the user left the group coversation.
for (ConversationListener listener : conversationListeners) { */
listener.conversationEnded(conversation); void leftGroupConversation(JID room, JID user, Date date) {
} Conversation conversation = getRoomConversation(room);
} if (conversation != null) {
conversation.participantLeft(user, date.getTime());
/** }
* Returns the group conversation taking place in the specified room or <tt>null</tt> if none. }
*
* @param room JID of the room. void roomConversationEnded(JID room, Date date) {
* @return the group conversation taking place in the specified room or null if none. Conversation conversation = getRoomConversation(room);
*/ if (conversation != null) {
private Conversation getRoomConversation(JID room) { removeConversation(room.toString(), conversation, date);
String conversationKey = room.toString(); }
return conversations.get(conversationKey); }
}
private void removeConversation(String key, Conversation conversation, Date date) {
private boolean isExternal(XMPPServer server, JID jid) { conversations.remove(key);
return !server.isLocal(jid) || gateways.contains(jid.getDomain()); // Notify conversation that it has ended
} conversation.conversationEnded(date);
// Notify listeners of the conversation ending.
/** for (ConversationListener listener : conversationListeners) {
* Returns true if the specified message should be processed by the conversation manager. listener.conversationEnded(conversation);
* Only messages between two users, group chats, or gateways are processed. }
* }
* @param message the message to analyze.
* @return true if the specified message should be processed by the conversation manager. /**
*/ * Returns the group conversation taking place in the specified room or <tt>null</tt> if none.
boolean isConversation(Message message) { *
if (Message.Type.normal == message.getType() || Message.Type.chat == message.getType()) { * @param room
// TODO: how should conversations with components on other servers be handled? * JID of the room.
return isConversationJID(message.getFrom()) && isConversationJID(message.getTo()); * @return the group conversation taking place in the specified room or null if none.
} */
return false; private Conversation getRoomConversation(JID room) {
} String conversationKey = room.toString();
return conversations.get(conversationKey);
/** }
* Returns true if the specified JID should be recorded in a conversation.
* private boolean isExternal(XMPPServer server, JID jid) {
* @param jid the JID. return !server.isLocal(jid) || gateways.contains(jid.getDomain());
* @return true if the JID should be recorded in a conversation. }
*/
private boolean isConversationJID(JID jid) { /**
// Ignore conversations when there is no jid * Returns true if the specified message should be processed by the conversation manager. Only messages between two users, group chats, or
if (jid == null) { * gateways are processed.
return false; *
} * @param message
XMPPServer server = XMPPServer.getInstance(); * the message to analyze.
if (jid.getNode() == null) { * @return true if the specified message should be processed by the conversation manager.
return false; */
} boolean isConversation(Message message) {
if (Message.Type.normal == message.getType() || Message.Type.chat == message.getType()) {
// Always accept local JIDs or JIDs related to gateways // TODO: how should conversations with components on other servers be handled?
// (this filters our components, MUC, pubsub, etc. except gateways). return isConversationJID(message.getFrom()) && isConversationJID(message.getTo());
if (server.isLocal(jid) || gateways.contains(jid.getDomain())) { }
return true; return false;
} }
// If not a local JID, always record it. /**
if (!jid.getDomain().endsWith(serverInfo.getXMPPDomain())) { * Returns true if the specified JID should be recorded in a conversation.
return true; *
} * @param jid
* the JID.
// Otherwise return false. * @return true if the JID should be recorded in a conversation.
return false; */
} private boolean isConversationJID(JID jid) {
// Ignore conversations when there is no jid
/** if (jid == null) {
* Returns a unique key for a coversation between two JID's. The order of two JID parameters return false;
* is irrelevant; the same key will be returned. }
* XMPPServer server = XMPPServer.getInstance();
* @param jid1 the first JID. if (jid.getNode() == null) {
* @param jid2 the second JID. return false;
* @return a unique key. }
*/
String getConversationKey(JID jid1, JID jid2) { // Always accept local JIDs or JIDs related to gateways
StringBuilder builder = new StringBuilder(); // (this filters our components, MUC, pubsub, etc. except gateways).
if (jid1.compareTo(jid2) < 0) { if (server.isLocal(jid) || gateways.contains(jid.getDomain())) {
builder.append(jid1.toBareJID()).append("_").append(jid2.toBareJID()); return true;
} }
else {
builder.append(jid2.toBareJID()).append("_").append(jid1.toBareJID()); // If not a local JID, always record it.
} if (!jid.getDomain().endsWith(serverInfo.getXMPPDomain())) {
return builder.toString(); return true;
} }
String getRoomConversationKey(JID roomJID) { // Otherwise return false.
return roomJID.toString(); return false;
} }
public void componentInfoReceived(IQ iq) { /**
//Check if the component is a gateway * Returns a unique key for a coversation between two JID's. The order of two JID parameters is irrelevant; the same key will be returned.
boolean gatewayFound = false; *
Element childElement = iq.getChildElement(); * @param jid1
for (Iterator<Element> it = childElement.elementIterator("identity"); it.hasNext();) { * the first JID.
Element identity = it.next(); * @param jid2
if ("gateway".equals(identity.attributeValue("category"))) { * the second JID.
gatewayFound = true; * @return a unique key.
} */
} String getConversationKey(JID jid1, JID jid2) {
// If component is a gateway then keep track of the component StringBuilder builder = new StringBuilder();
if (gatewayFound) { if (jid1.compareTo(jid2) < 0) {
gateways.add(iq.getFrom().getDomain()); builder.append(jid1.toBareJID()).append("_").append(jid2.toBareJID());
} } else {
} builder.append(jid2.toBareJID()).append("_").append(jid1.toBareJID());
}
public void componentRegistered(JID componentJID) { return builder.toString();
//Do nothing }
}
String getRoomConversationKey(JID roomJID) {
public void componentUnregistered(JID componentJID) { return roomJID.toString();
// Remove stored information about this component }
gateways.remove(componentJID.getDomain());
} public void componentInfoReceived(IQ iq) {
// Check if the component is a gateway
void queueParticipantLeft(Conversation conversation, JID user, ConversationParticipation participation) { boolean gatewayFound = false;
RoomParticipant updatedParticipant = new RoomParticipant(); Element childElement = iq.getChildElement();
updatedParticipant.conversationID = conversation.getConversationID(); for (Iterator<Element> it = childElement.elementIterator("identity"); it.hasNext();) {
updatedParticipant.user = user; Element identity = it.next();
updatedParticipant.joined = participation.getJoined(); if ("gateway".equals(identity.attributeValue("category"))) {
updatedParticipant.left = participation.getLeft(); gatewayFound = true;
participantQueue.add(updatedParticipant); }
} }
// If component is a gateway then keep track of the component
/** if (gatewayFound) {
* A task that persists conversation meta-data and messages to the database. gateways.add(iq.getFrom().getDomain());
*/ }
private class ArchivingTask implements Runnable { }
public void run() { public void componentRegistered(JID componentJID) {
synchronized (this) { // Do nothing
if (archivingRunning) { }
return;
} public void componentUnregistered(JID componentJID) {
archivingRunning = true; // Remove stored information about this component
} gateways.remove(componentJID.getDomain());
if (!messageQueue.isEmpty() || !conversationQueue.isEmpty() || !participantQueue.isEmpty()) { }
Connection con = null;
PreparedStatement pstmt = null; void queueParticipantLeft(Conversation conversation, JID user, ConversationParticipation participation) {
try { RoomParticipant updatedParticipant = new RoomParticipant();
con = DbConnectionManager.getConnection(); updatedParticipant.conversationID = conversation.getConversationID();
updatedParticipant.user = user;
pstmt = con.prepareStatement(INSERT_MESSAGE); updatedParticipant.joined = participation.getJoined();
ArchivedMessage message; updatedParticipant.left = participation.getLeft();
int count = 0; participantQueue.add(updatedParticipant);
while ((message = messageQueue.poll()) != null) { }
pstmt.setLong(1, message.getConversationID());
pstmt.setString(2, message.getFromJID().toBareJID()); /**
pstmt.setString(3, message.getFromJID().getResource()); * A task that persists conversation meta-data and messages to the database.
pstmt.setString(4, message.getToJID().toBareJID()); */
pstmt.setString(5, message.getToJID().getResource()); private class ArchivingTask implements Runnable {
pstmt.setLong(6, message.getSentDate().getTime());
DbConnectionManager.setLargeTextField(pstmt, 7, message.getBody()); public void run() {
if (DbConnectionManager.isBatchUpdatesSupported()) { synchronized (this) {
pstmt.addBatch(); if (archivingRunning) {
} return;
else { }
pstmt.execute(); archivingRunning = true;
} }
// Only batch up to 500 items at a time. if (!messageQueue.isEmpty() || !conversationQueue.isEmpty() || !participantQueue.isEmpty()) {
if (count % 500 == 0 && DbConnectionManager.isBatchUpdatesSupported()) { Connection con = null;
pstmt.executeBatch(); PreparedStatement pstmt = null;
} try {
count++; con = DbConnectionManager.getConnection();
}
if (DbConnectionManager.isBatchUpdatesSupported()) { pstmt = con.prepareStatement(INSERT_MESSAGE);
pstmt.executeBatch(); ArchivedMessage message;
} int count = 0;
while ((message = messageQueue.poll()) != null) {
pstmt = con.prepareStatement(UPDATE_CONVERSATION); pstmt.setLong(1, message.getConversationID());
Conversation conversation; pstmt.setString(2, message.getFromJID().toBareJID());
count = 0; pstmt.setString(3, message.getFromJID().getResource());
while ((conversation = conversationQueue.poll()) != null) { pstmt.setString(4, message.getToJID().toBareJID());
pstmt.setLong(1, conversation.getLastActivity().getTime()); pstmt.setString(5, message.getToJID().getResource());
pstmt.setInt(2, conversation.getMessageCount()); pstmt.setLong(6, message.getSentDate().getTime());
pstmt.setLong(3, conversation.getConversationID()); DbConnectionManager.setLargeTextField(pstmt, 7, message.getBody());
if (DbConnectionManager.isBatchUpdatesSupported()) { if (DbConnectionManager.isBatchUpdatesSupported()) {
pstmt.addBatch(); pstmt.addBatch();
} } else {
else { pstmt.execute();
pstmt.execute(); }
} // Only batch up to 500 items at a time.
// Only batch up to 500 items at a time. if (count % 500 == 0 && DbConnectionManager.isBatchUpdatesSupported()) {
if (count % 500 == 0 && DbConnectionManager.isBatchUpdatesSupported()) { pstmt.executeBatch();
pstmt.executeBatch(); }
} count++;
count++; }
} if (DbConnectionManager.isBatchUpdatesSupported()) {
if (DbConnectionManager.isBatchUpdatesSupported()) { pstmt.executeBatch();
pstmt.executeBatch(); }
}
pstmt = con.prepareStatement(UPDATE_CONVERSATION);
pstmt = con.prepareStatement(UPDATE_PARTICIPANT); Conversation conversation;
RoomParticipant particpiant; count = 0;
count = 0; while ((conversation = conversationQueue.poll()) != null) {
while ((particpiant = participantQueue.poll()) != null) { pstmt.setLong(1, conversation.getLastActivity().getTime());
pstmt.setLong(1, particpiant.left.getTime()); pstmt.setInt(2, conversation.getMessageCount());
pstmt.setLong(2, particpiant.conversationID); pstmt.setLong(3, conversation.getConversationID());
pstmt.setString(3, particpiant.user.toBareJID()); if (DbConnectionManager.isBatchUpdatesSupported()) {
pstmt.setString(4, particpiant.user.getResource() == null ? " " : particpiant.user.getResource()); pstmt.addBatch();
pstmt.setLong(5, particpiant.joined.getTime()); } else {
if (DbConnectionManager.isBatchUpdatesSupported()) { pstmt.execute();
pstmt.addBatch(); }
} // Only batch up to 500 items at a time.
else { if (count % 500 == 0 && DbConnectionManager.isBatchUpdatesSupported()) {
pstmt.execute(); pstmt.executeBatch();
} }
// Only batch up to 500 items at a time. count++;
if (count % 500 == 0 && DbConnectionManager.isBatchUpdatesSupported()) { }
pstmt.executeBatch(); if (DbConnectionManager.isBatchUpdatesSupported()) {
} pstmt.executeBatch();
count++; }
}
if (DbConnectionManager.isBatchUpdatesSupported()) { pstmt = con.prepareStatement(UPDATE_PARTICIPANT);
pstmt.executeBatch(); RoomParticipant particpiant;
} count = 0;
} while ((particpiant = participantQueue.poll()) != null) {
catch (Exception e) { pstmt.setLong(1, particpiant.left.getTime());
Log.error(e.getMessage(), e); pstmt.setLong(2, particpiant.conversationID);
} pstmt.setString(3, particpiant.user.toBareJID());
finally { pstmt.setString(4, particpiant.user.getResource() == null ? " " : particpiant.user.getResource());
DbConnectionManager.closeConnection(pstmt, con); pstmt.setLong(5, particpiant.joined.getTime());
} if (DbConnectionManager.isBatchUpdatesSupported()) {
} pstmt.addBatch();
// Set archiving running back to false. } else {
archivingRunning = false; pstmt.execute();
} }
} // Only batch up to 500 items at a time.
if (count % 500 == 0 && DbConnectionManager.isBatchUpdatesSupported()) {
/** pstmt.executeBatch();
* A PropertyEventListener that tracks updates to Jive properties that are related }
* to conversation tracking and archiving. count++;
*/ }
private class ConversationPropertyListener implements PropertyEventListener { if (DbConnectionManager.isBatchUpdatesSupported()) {
pstmt.executeBatch();
public void propertySet(String property, Map<String, Object> params) { }
if (property.equals("conversation.metadataArchiving")) { } catch (Exception e) {
String value = (String)params.get("value"); Log.error(e.getMessage(), e);
metadataArchivingEnabled = Boolean.valueOf(value); } finally {
} DbConnectionManager.closeConnection(pstmt, con);
else if (property.equals("conversation.messageArchiving")) { }
String value = (String)params.get("value"); }
messageArchivingEnabled = Boolean.valueOf(value); // Set archiving running back to false.
// Force metadata archiving enabled on if message archiving on. archivingRunning = false;
if (messageArchivingEnabled) { }
metadataArchivingEnabled = true; }
}
} /**
else if (property.equals("conversation.roomArchiving")) { * A PropertyEventListener that tracks updates to Jive properties that are related to conversation tracking and archiving.
String value = (String)params.get("value"); */
roomArchivingEnabled = Boolean.valueOf(value); private class ConversationPropertyListener implements PropertyEventListener {
// Force metadata archiving enabled on if message archiving on.
if (roomArchivingEnabled) { public void propertySet(String property, Map<String, Object> params) {
metadataArchivingEnabled = true; if (property.equals("conversation.metadataArchiving")) {
} String value = (String) params.get("value");
} metadataArchivingEnabled = Boolean.valueOf(value);
else if (property.equals("conversation.roomsArchived")) { } else if (property.equals("conversation.messageArchiving")) {
String value = (String)params.get("value"); String value = (String) params.get("value");
roomsArchived = StringUtils.stringToCollection(value); messageArchivingEnabled = Boolean.valueOf(value);
} // Force metadata archiving enabled on if message archiving on.
else if (property.equals("conversation.idleTime")) { if (messageArchivingEnabled) {
String value = (String)params.get("value"); metadataArchivingEnabled = true;
try { }
idleTime = Integer.parseInt(value) * JiveConstants.MINUTE; } else if (property.equals("conversation.roomArchiving")) {
} String value = (String) params.get("value");
catch (Exception e) { roomArchivingEnabled = Boolean.valueOf(value);
Log.error(e.getMessage(), e); // Force metadata archiving enabled on if message archiving on.
idleTime = DEFAULT_IDLE_TIME * JiveConstants.MINUTE; if (roomArchivingEnabled) {
} metadataArchivingEnabled = true;
} }
else if (property.equals("conversation.maxTime")) { } else if (property.equals("conversation.roomsArchived")) {
String value = (String)params.get("value"); String value = (String) params.get("value");
try { roomsArchived = StringUtils.stringToCollection(value);
maxTime = Integer.parseInt(value) * JiveConstants.MINUTE; } else if (property.equals("conversation.idleTime")) {
} String value = (String) params.get("value");
catch (Exception e) { try {
Log.error(e.getMessage(), e); idleTime = Integer.parseInt(value) * JiveConstants.MINUTE;
maxTime = DEFAULT_MAX_TIME * JiveConstants.MINUTE; } catch (Exception e) {
} Log.error(e.getMessage(), e);
} idleTime = DEFAULT_IDLE_TIME * JiveConstants.MINUTE;
} }
} else if (property.equals("conversation.maxTime")) {
public void propertyDeleted(String property, Map<String, Object> params) { String value = (String) params.get("value");
if (property.equals("conversation.metadataArchiving")) { try {
metadataArchivingEnabled = true; maxTime = Integer.parseInt(value) * JiveConstants.MINUTE;
} } catch (Exception e) {
else if (property.equals("conversation.messageArchiving")) { Log.error(e.getMessage(), e);
messageArchivingEnabled = false; maxTime = DEFAULT_MAX_TIME * JiveConstants.MINUTE;
} }
else if (property.equals("conversation.roomArchiving")) { } else if (property.equals("conversation.maxRetrievable")) {
roomArchivingEnabled = false; String value = (String) params.get("value");
} try {
else if (property.equals("conversation.roomsArchived")) { maxRetrievable = Integer.parseInt(value) * JiveConstants.DAY;
roomsArchived = Collections.emptyList(); } catch (Exception e) {
} Log.error(e.getMessage(), e);
else if (property.equals("conversation.idleTime")) { maxRetrievable = DEFAULT_MAX_RETRIEVABLE * JiveConstants.DAY;
idleTime = DEFAULT_IDLE_TIME * JiveConstants.MINUTE; }
} } else if (property.equals("conversation.maxAge")) {
else if (property.equals("conversation.maxTime")) { String value = (String) params.get("value");
maxTime = DEFAULT_MAX_TIME * JiveConstants.MINUTE; try {
} maxAge = Integer.parseInt(value) * JiveConstants.DAY;
} } catch (Exception e) {
Log.error(e.getMessage(), e);
public void xmlPropertySet(String property, Map<String, Object> params) { maxAge = DEFAULT_MAX_AGE * JiveConstants.DAY;
// Ignore. }
} }
public void xmlPropertyDeleted(String property, Map<String, Object> params) { }
// Ignore.
} public void propertyDeleted(String property, Map<String, Object> params) {
} if (property.equals("conversation.metadataArchiving")) {
metadataArchivingEnabled = true;
private static class RoomParticipant { } else if (property.equals("conversation.messageArchiving")) {
private long conversationID = -1; messageArchivingEnabled = false;
private JID user; } else if (property.equals("conversation.roomArchiving")) {
private Date joined; roomArchivingEnabled = false;
private Date left; } else if (property.equals("conversation.roomsArchived")) {
} roomsArchived = Collections.emptyList();
} else if (property.equals("conversation.idleTime")) {
idleTime = DEFAULT_IDLE_TIME * JiveConstants.MINUTE;
} else if (property.equals("conversation.maxTime")) {
maxTime = DEFAULT_MAX_TIME * JiveConstants.MINUTE;
} else if (property.equals("conversation.maxAge")) {
maxAge = DEFAULT_MAX_AGE * JiveConstants.DAY;
} else if (property.equals("conversation.maxRetrievable")) {
maxRetrievable = DEFAULT_MAX_RETRIEVABLE * JiveConstants.DAY;
}
}
public void xmlPropertySet(String property, Map<String, Object> params) {
// Ignore.
}
public void xmlPropertyDeleted(String property, Map<String, Object> params) {
// Ignore.
}
}
private static class RoomParticipant {
private long conversationID = -1;
private JID user;
private Date joined;
private Date left;
}
} }
\ No newline at end of file
...@@ -163,6 +163,10 @@ ...@@ -163,6 +163,10 @@
boolean roomArchiving = conversationManager.isRoomArchivingEnabled(); boolean roomArchiving = conversationManager.isRoomArchivingEnabled();
int idleTime = ParamUtils.getIntParameter(request, "idleTime", conversationManager.getIdleTime()); int idleTime = ParamUtils.getIntParameter(request, "idleTime", conversationManager.getIdleTime());
int maxTime = ParamUtils.getIntParameter(request, "maxTime", conversationManager.getMaxTime()); int maxTime = ParamUtils.getIntParameter(request, "maxTime", conversationManager.getMaxTime());
int maxAge = ParamUtils.getIntParameter(request, "maxAge", conversationManager.getMaxAge());
int maxRetrievable = ParamUtils.getIntParameter(request, "maxRetrievable", conversationManager.getMaxRetrievable());
boolean rebuildIndex = request.getParameter("rebuild") != null; boolean rebuildIndex = request.getParameter("rebuild") != null;
if (request.getParameter("cancel") != null) { if (request.getParameter("cancel") != null) {
...@@ -197,6 +201,14 @@ ...@@ -197,6 +201,14 @@
errors.put("roomsArchived", ""); errors.put("roomsArchived", "");
errorMessage = "Only name of local rooms should be specified."; errorMessage = "Only name of local rooms should be specified.";
} }
if (maxAge < 0) {
errors.put("maxAge", "");
errorMessage = "Max Age must be greater than or equal to 0.";
}
if (maxRetrievable < 1) {
errors.put("maxRetrievable", "");
errorMessage = "Max Retrievable must be greater than or equal to 0.";
}
// If no errors, continue: // If no errors, continue:
if (errors.size() == 0) { if (errors.size() == 0) {
conversationManager.setMetadataArchivingEnabled(metadataArchiving); conversationManager.setMetadataArchivingEnabled(metadataArchiving);
...@@ -205,6 +217,9 @@ ...@@ -205,6 +217,9 @@
conversationManager.setRoomsArchived(StringUtils.stringToCollection(roomsArchived)); conversationManager.setRoomsArchived(StringUtils.stringToCollection(roomsArchived));
conversationManager.setIdleTime(idleTime); conversationManager.setIdleTime(idleTime);
conversationManager.setMaxTime(maxTime); conversationManager.setMaxTime(maxTime);
conversationManager.setMaxAge(maxAge);
conversationManager.setMaxRetrievable(maxRetrievable);
%> %>
<div class="success"> <div class="success">
...@@ -285,6 +300,21 @@ ...@@ -285,6 +300,21 @@
<td><input type="text" name="maxTime" size="10" maxlength="10" value="<%= conversationManager.getMaxTime()%>" /></td> <td><input type="text" name="maxTime" size="10" maxlength="10" value="<%= conversationManager.getMaxTime()%>" /></td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td><label class="jive-label"><fmt:message key="archive.settings.max.age"/>:</label><br>
<fmt:message key="archive.settings.max.age.description"/><br><br></td>
<td><input type="text" name="maxAge" size="10" maxlength="10" value="<%= conversationManager.getMaxAge()%>" /></td>
<td></td>
</tr>
<tr>
<td><label class="jive-label"><fmt:message key="archive.settings.max.retrievable"/>:</label><br>
<fmt:message key="archive.settings.max.retrievable.description"/><br><br></td>
<td><input type="text" name="maxRetrievable" size="10" maxlength="10" value="<%= conversationManager.getMaxRetrievable()%>" /></td>
<td></td>
</tr>
</tbody> </tbody>
</table> </table>
......
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