Skip to content

Commit

Permalink
#369 and #370 Clear Chat Room History
Browse files Browse the repository at this point in the history
Added MUCRoomStatusDAO to house all database access procedures for ofMucRoomStatus:
Refactor ConversationManager to use MUCRoomStatusDAO for room status management

- Moved `UPDATE_ROOM_DESTROYED_STATUS` and `LOAD_ROOM_INFO` SQL queries from `ConversationManager` into MUCRoomStatusDAO.
- Updated `getRoomIDFromRoomJID` method to handle exceptions and return `NO_ROOM_ID` if room ID is not found.
- Added `roomCreated` method to insert new room info using `MUCRoomStatusDAO`.
- Updated `roomDestroyed` method to use `MUCRoomStatusDAO` for updating room destroyed status and removing room from `roomJIDToIDMap`.
- Added code to `clearChatHistory` method to rebuild archive and MUC Lucene index after clearing chat history.
  • Loading branch information
“Huy committed Dec 11, 2024
1 parent 3dddfbc commit 577d63a
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 81 deletions.
32 changes: 0 additions & 32 deletions src/java/org/jivesoftware/openfire/archive/ConversationDAO.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
*/
public class ConversationDAO {

private static final String INSERT_NEW_ROOM = "INSERT INTO ofMucRoomStatus (roomID, roomJID, roomDestroyed) VALUES (?, ?, 0)";
private static final String INSERT_CONVERSATION = "INSERT INTO ofConversation(roomID, conversationID, room, isExternal, startDate, "
+ "lastActivity, messageCount) VALUES (?,?,?,?,?,?,0)";
private static final String INSERT_PARTICIPANT = "INSERT INTO ofConParticipant(roomID, conversationID, joinedDate, bareJID, jidResource, nickname) "
Expand Down Expand Up @@ -118,16 +117,6 @@ public static Conversation createConversation(ConversationManager conversationMa
}
}

// Add the room to the ofMucRoomStatus table if it doesn't already exist
if (!ConversationManager.hasRoomJIDToIDMap(roomJID)) {
long roomID = conversationManager.getRoomIDFromRoomJID(roomJID);

try {
insertNewRoomIntoStatusTable(roomID, roomJID.toString());
} catch (Exception e) {
Log.error("Unable to insert a new room into the ofMucRoomStatus table: roomID={}, roomJID={}", roomID, roomJID, e);
}
}
long roomID = conversationManager.getRoomIDFromRoomJID(roomJID);
final Conversation conversation = new Conversation(roomID, roomJID, participants, external, startDate);

Expand Down Expand Up @@ -303,27 +292,6 @@ private static Conversation loadFromDb(final long conversationID) throws NotFoun
}
}

/**
* Inserts a new row into the ofMucRoomStatus table.
*
* @param roomID the ID of the room.
* @param roomJID the JID of the room.
* @throws SQLException if an error occurs inserting the row.
*/
public static void insertNewRoomIntoStatusTable(long roomID, String roomJID) throws SQLException {
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(INSERT_NEW_ROOM);
pstmt.setLong(1, roomID);
pstmt.setString(2, roomJID);
pstmt.executeUpdate();
} finally {
DbConnectionManager.closeConnection(pstmt, con);
}
}

/**
* Inserts a new conversation into the database.
*
Expand Down
14 changes: 14 additions & 0 deletions src/java/org/jivesoftware/openfire/archive/ConversationEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ public void run(ConversationManager conversationManager) {
if (Type.chatMessageReceived == type) {
conversationManager.processMessage(sender, receiver, body, stanza, date);
}
else if (Type.roomCreated == type) {
conversationManager.roomCreated(roomJID);
}
else if (Type.roomDestroyed == type) {
conversationManager.roomDestroyed(roomJID, date);
}
Expand Down Expand Up @@ -108,6 +111,13 @@ public static ConversationEvent chatMessageReceived(JID sender, JID receiver, St
return event;
}

public static ConversationEvent roomCreated(JID roomJID) {
ConversationEvent event = new ConversationEvent();
event.type = Type.roomCreated;
event.roomJID = roomJID;
return event;
}

public static ConversationEvent roomDestroyed(JID roomJID, Date date) {
ConversationEvent event = new ConversationEvent();
event.type = Type.roomDestroyed;
Expand Down Expand Up @@ -167,6 +177,10 @@ public static ConversationEvent roomMessageReceived(JID roomJID, JID user, JID r
}

private enum Type {
/**
* Event triggered when a new room was created.
*/
roomCreated,
/**
* Event triggered when a room was destroyed.
*/
Expand Down
74 changes: 26 additions & 48 deletions src/java/org/jivesoftware/openfire/archive/ConversationManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.database.JiveID;
import org.jivesoftware.database.SequenceManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.XMPPServerInfo;
Expand Down Expand Up @@ -80,7 +81,6 @@ public class ConversationManager implements ComponentEventListener{

private static final String UPDATE_CONVERSATION = "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_ROOM_DESTROYED_STATUS = "UPDATE ofMucRoomStatus SET roomDestroyed=1 WHERE roomID=?";
private static final String INSERT_MESSAGE = "INSERT INTO ofMessageArchive(roomID, messageID, conversationID, fromJID, fromJIDResource, toJID, toJIDResource, sentDate, body, stanza, isPMforJID) "
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?)";
private static final String CONVERSATION_COUNT = "SELECT COUNT(*) FROM ofConversation";
Expand All @@ -91,7 +91,6 @@ public class ConversationManager implements ComponentEventListener{
private static final String DELETE_ARCHIVE_ROOM_CHAT_HISTORY = "DELETE FROM ofMessageArchive WHERE roomID=?";
private static final String DELETE_ALL_ROOM_PARTICIPANTS = "DELETE FROM ofConParticipant WHERE roomID=?";
private static final String DELETE_ALL_ROOM_CONVERSATIONS = "DELETE FROM ofConversation WHERE roomID=?";
private static final String LOAD_ROOM_INFO = "SELECT roomID, roomJID FROM ofMucRoomStatus WHERE roomDestroyed=0";

private static final Duration DEFAULT_IDLE_TIME = Duration.ofMinutes(10);
private static final Duration DEFAULT_MAX_TIME = Duration.ofMinutes(60);
Expand All @@ -112,11 +111,7 @@ public class ConversationManager implements ComponentEventListener{
* A room JIDs can be reuse if a room is destroyed and then recreated.
* The unique room IDs however are kept unique using a SequenceManager instance.
*/
private static final Map<JID, Long> roomJIDToIDMap = new ConcurrentHashMap<>();

static {
populateRoomJIDToIDMap();
}
private static Map<JID, Long> roomJIDToIDMap = MUCRoomStatusDAO.loadRoomJIDToIDMap();

/**
* Stores the mapping between active conversation IDs and room IDs.
Expand All @@ -125,7 +120,7 @@ public class ConversationManager implements ComponentEventListener{
/**
* A SequenceManager instance used to generate unique room IDs for the Monitoring Plugin.
*/
private static final SequenceManager roomIDSequenceManager = SequenceManager.getSequenceManagerByUniqueName("Monitoring Plugin - RoomID");
private static final SequenceManager roomIDSequenceManager = new SequenceManager(655, 50);

private boolean metadataArchivingEnabled;

Expand Down Expand Up @@ -238,29 +233,6 @@ public ConversationManager(TaskEngine taskEngine) {

}

/**
* Populates the `roomJIDToIDMap` with room IDs and their corresponding JIDs from the database.
*/
private static void populateRoomJIDToIDMap() {
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_ROOM_INFO);
rs = pstmt.executeQuery();
while (rs.next()) {
long roomID = rs.getLong("roomID");
JID roomJID = new JID(rs.getString("roomJID"));
roomJIDToIDMap.put(roomJID, roomID);
}
} catch (SQLException e) {
Log.error("Error populating roomJIDToIDMap", e);
} finally {
DbConnectionManager.closeConnection(rs, pstmt, con);
}
}

/**
* Checks if the `roomJIDToIDMap` contains the specified room JID.
*
Expand All @@ -278,7 +250,13 @@ public static boolean hasRoomJIDToIDMap(JID roomJID) {
* @return the unique room ID.
*/
public static long getRoomIDFromRoomJID(JID roomJID) {
return roomJIDToIDMap.computeIfAbsent(roomJID, jid -> roomIDSequenceManager.nextUniqueID());
try {
return roomJIDToIDMap.get(roomJID);
} catch (Exception e) {
Log.error("Error getting roomID from roomJID", e);
}

return NO_ROOM_ID;
}

public static long getRoomIDFromConversationID(long conversationID) {
Expand Down Expand Up @@ -475,6 +453,11 @@ public void clearChatHistory(JID roomJID) {
DbConnectionManager.closeConnection(pstmtDeleteParticipants, con);
DbConnectionManager.closeConnection(pstmtDeleteConversations, con);
}

// Rebuild the archive and muc lucene index
MonitoringPlugin plugin = MonitoringPlugin.getInstance();
plugin.getArchiveIndexer().rebuildIndex();
plugin.getMucIndexer().rebuildIndex();
}

/**
Expand Down Expand Up @@ -1025,26 +1008,21 @@ void leftGroupConversation(JID room, JID user, Date date) {
}
}

void roomDestroyed(JID room, Date date) {
// Update the room status to destroyed in the database
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(UPDATE_ROOM_DESTROYED_STATUS);
pstmt.setLong(1, getRoomIDFromRoomJID(room));
pstmt.executeUpdate();
} catch (SQLException e) {
Log.error("Error updating room destroyed status for room: " + room, e);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
void roomCreated(JID roomJID) {
long roomID = roomIDSequenceManager.nextUniqueID();
if (MUCRoomStatusDAO.hasInsertedRoomInfo(roomID, roomJID)) {
roomJIDToIDMap.put(roomJID, roomID);
}
}

// Remove the room from the roomID map
roomJIDToIDMap.remove(room);
void roomDestroyed(JID roomJID, Date date) {
if(MUCRoomStatusDAO.hasUpdatedRoomDestroyedStatus(getRoomIDFromRoomJID(roomJID))) {
// Remove the room from the roomID map
roomJIDToIDMap.remove(roomJID);
}

// End existing conversation
roomConversationEnded(room, date);
roomConversationEnded(roomJID, date);
}

void roomConversationEnded(JID room, Date date) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,15 @@ public GroupConversationInterceptor(ConversationManager conversationManager) {

@Override
public void roomCreated(JID roomJID) {
//Do nothing
// Process this event in the senior cluster member or local JVM when not in a cluster
if (ClusterManager.isSeniorClusterMember()) {
conversationManager.roomCreated(roomJID);
}
else {
ConversationEventsQueue eventsQueue = conversationManager.getConversationEventsQueue();
eventsQueue.addGroupChatEvent(conversationManager.getRoomConversationKey(roomJID),
ConversationEvent.roomCreated(roomJID));
}
}

@Override
Expand Down
126 changes: 126 additions & 0 deletions src/java/org/jivesoftware/openfire/archive/MUCRoomStatusDAO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright (C) 2024 Ignite Realtime Foundation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.archive;

import org.jivesoftware.database.DbConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* A database access object for ofMucRoomStatus table.
*
* @author Huy Vu, [email protected]
*/
public class MUCRoomStatusDAO {
private static final Logger Log = LoggerFactory.getLogger(MUCRoomStatusDAO.class);

private static final String INSERT_NEW_ROOM = "INSERT INTO ofMucRoomStatus (roomID, roomJID, roomDestroyed) VALUES (?, ?, 0)";
private static final String UPDATE_ROOM_DESTROYED_STATUS = "UPDATE ofMucRoomStatus SET roomDestroyed=1 WHERE roomID=?";
private static final String LOAD_ROOM_INFO = "SELECT roomID, roomJID FROM ofMucRoomStatus WHERE roomDestroyed=0";

/**
* Loads the room JID to room ID map from the database.
*
* This method retrieves all active (not destroyed) rooms from the `ofMucRoomStatus` table
* and populates a map with the room JID as the key and the room ID as the value.
*
* @return A map where the key is the room JID and the value is the room ID.
*/
public static Map<JID, Long> loadRoomJIDToIDMap() {
Map<JID, Long> roomJIDToIDMap = new ConcurrentHashMap<>();

Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_ROOM_INFO);
rs = pstmt.executeQuery();
while (rs.next()) {
long roomID = rs.getLong("roomID");
JID roomJID = new JID(rs.getString("roomJID"));
roomJIDToIDMap.put(roomJID, roomID);
}
} catch (SQLException e) {
Log.error("Error loading room info", e);
} finally {
DbConnectionManager.closeConnection(rs, pstmt, con);
}

return roomJIDToIDMap;
}

/**
* Inserts a new room information into the database.
*
* This method inserts a new room entry into the `ofMucRoomStatus` table with the given room ID and room JID.
*
* @param roomID The ID of the room to be inserted.
* @param roomJID The JID of the room to be inserted.
* @return true if the room information was successfully inserted, false otherwise.
*/
public static boolean hasInsertedRoomInfo(long roomID, JID roomJID) {
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(INSERT_NEW_ROOM);
pstmt.setLong(1, roomID);
pstmt.setString(2, roomJID.toString());
pstmt.executeUpdate();
return true;
} catch (SQLException sqle) {
Log.error("An exception occurred while trying to create a new room {} status.", roomJID, sqle);
}
finally {
DbConnectionManager.closeConnection(pstmt, con);
}
return false;
}

/**
* Updates the room status to destroyed in the database.
*
* This method sets the `roomDestroyed` status to 1 for the specified room ID in the `ofMucRoomStatus` table.
*
* @param roomID The ID of the room to be marked as destroyed.
* @return true if the room status was successfully updated, false otherwise.
*/
public static boolean hasUpdatedRoomDestroyedStatus(long roomID) {
// Update the room status to destroyed in the database
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(UPDATE_ROOM_DESTROYED_STATUS);
pstmt.setLong(1, roomID);
pstmt.executeUpdate();
return true;
} catch (SQLException e) {
Log.error("Error updating room destroyed status for room with id: " + roomID, e);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
}
return false;
}
}

0 comments on commit 577d63a

Please sign in to comment.