From 3facece9c22caedbf0a5f35f9b17457a748447a1 Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Tue, 27 Feb 2024 18:46:56 +0100 Subject: [PATCH] Moving shared MAM Query representation into abstract class The Monitoring Service plugin's XEP-0313: Message Archive Management implementation contains at least six similar query implementations. The code that is duplicated between them is moved to abstract classes, that the original code now inherits from. This reduces code duplication and makes it easier to add new query types. --- .../impl/AbstractPaginatedMamMucQuery.java | 110 ++++++++++++ .../impl/AbstractPaginatedMamQuery.java | 158 ++++++++++++++++++ .../impl/MucMamPersistenceManager.java | 4 +- .../impl/PaginatedMessageDatabaseQuery.java | 81 +++------ .../impl/PaginatedMessageLuceneQuery.java | 48 +++--- .../PaginatedMucMessageDatabaseQuery.java | 68 ++------ ...edMucMessageFromOpenfireDatabaseQuery.java | 54 +++--- ...atedMucMessageFromOpenfireLuceneQuery.java | 47 +++--- .../impl/PaginatedMucMessageLuceneQuery.java | 44 ++--- 9 files changed, 399 insertions(+), 215 deletions(-) create mode 100644 src/java/com/reucon/openfire/plugin/archive/impl/AbstractPaginatedMamMucQuery.java create mode 100644 src/java/com/reucon/openfire/plugin/archive/impl/AbstractPaginatedMamQuery.java diff --git a/src/java/com/reucon/openfire/plugin/archive/impl/AbstractPaginatedMamMucQuery.java b/src/java/com/reucon/openfire/plugin/archive/impl/AbstractPaginatedMamMucQuery.java new file mode 100644 index 000000000..e2f6c3053 --- /dev/null +++ b/src/java/com/reucon/openfire/plugin/archive/impl/AbstractPaginatedMamMucQuery.java @@ -0,0 +1,110 @@ +/* + * 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 com.reucon.openfire.plugin.archive.impl; + +import org.jivesoftware.openfire.muc.MUCRoom; +import org.xmpp.packet.JID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Date; + +/** + * Representation of a MAM query that is querying a multi-user chat-based archive. + * + * @author Guus der Kinderen, guus@goodbytes.nl + */ +public abstract class AbstractPaginatedMamMucQuery extends AbstractPaginatedMamQuery +{ + /** + * The MUC room instance that owns the message archive. + */ + @Nonnull + protected final MUCRoom room; + + /** + * To be able to return not only the public messages exchanged in the room, but also the private messages that are + * relevant for the user that performs this query, an additional JID is provided as the 'messageOwner' argument. + * This identifies the user for which to retrieve the (private) messages. + */ + @Nonnull + protected final JID messageOwner; + + /** + * Creates a query for messages from a message archive. + *

+ * To be able to return not only the public messages exchanged in the room, but also the private messages that are + * relevant for the user that performs this query, an additional JID is provided as the 'messageOwner' argument. + * This identifies the user for which to retrieve the (private) messages. + * + * @param startDate Start (inclusive) of period for which to return messages. EPOCH will be used if no value is provided. + * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. + * @param room The multi-user chat room that is the message archive owner. + * @param messageOwner The entity for which to return messages (typically the JID of the entity making the request). + * @param with An optional conversation partner + */ + public AbstractPaginatedMamMucQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final MUCRoom room, @Nonnull final JID messageOwner, @Nullable final JID with) + { + super(startDate, endDate, room.getJID(), with); + this.messageOwner = messageOwner; + this.room = room; + } + + /** + * Creates a query for messages from a message archive. + *

+ * To be able to return not only the public messages exchanged in the room, but also the private messages that are + * relevant for the user that performs this query, an additional JID is provided as the 'messageOwner' argument. + * This identifies the user for which to retrieve the (private) messages. + * + * @param startDate Start (inclusive) of period for which to return messages. EPOCH will be used if no value is provided. + * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. + * @param room The multi-user chat room that is the message archive owner. + * @param messageOwner The entity for which to return messages (typically the JID of the entity making the request). + * @param with An optional conversation partner + * @param query A search string to be used for text-based search. + */ + public AbstractPaginatedMamMucQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final MUCRoom room, @Nonnull final JID messageOwner, @Nullable final JID with, @Nonnull final String query) + { + super(startDate, endDate, room.getJID(), with, query); + this.messageOwner = messageOwner; + this.room = room; + } + + @Nonnull + public MUCRoom getRoom() + { + return room; + } + + @Nonnull + public JID getMessageOwner() + { + return messageOwner; + } + + @Override + public String toString() + { + return "AbstractPaginatedMamMucQuery{" + + "messageOwner=" + messageOwner + + ", startDate=" + startDate + + ", endDate=" + endDate + + ", archiveOwner=" + archiveOwner + + ", with=" + with + + ", query='" + query + '\'' + + '}'; + } +} diff --git a/src/java/com/reucon/openfire/plugin/archive/impl/AbstractPaginatedMamQuery.java b/src/java/com/reucon/openfire/plugin/archive/impl/AbstractPaginatedMamQuery.java new file mode 100644 index 000000000..981c05596 --- /dev/null +++ b/src/java/com/reucon/openfire/plugin/archive/impl/AbstractPaginatedMamQuery.java @@ -0,0 +1,158 @@ +/* + * 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 com.reucon.openfire.plugin.archive.impl; + +import com.reucon.openfire.plugin.archive.model.ArchivedMessage; +import org.xmpp.packet.JID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Date; +import java.util.List; + +/** + * Representation of a MAM query. + * + * @author Guus der Kinderen, guus@goodbytes.nl + */ +public abstract class AbstractPaginatedMamQuery +{ + /** + * Start (inclusive) of period for which to return messages. + */ + @Nonnull + protected final Date startDate; + + /** + * End (inclusive) of period for which to return messages. + */ + @Nonnull + protected final Date endDate; + + /** + * The message archive owner. + */ + @Nonnull + protected final JID archiveOwner; + + /** + * An optional conversation partner. + */ + @Nullable + protected final JID with; + + /** + * A search filter to be used for text-based search. + */ + @Nullable + protected final String query; + + /** + * Creates a query for messages from a message archive. + * + * @param startDate Start (inclusive) of period for which to return messages. EPOCH will be used if no value is provided. + * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. + * @param archiveOwner The message archive owner. + * @param with An optional conversation partner + */ + public AbstractPaginatedMamQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final JID archiveOwner, @Nullable final JID with) + { + this.startDate = startDate == null ? new Date( 0L ) : startDate ; + this.endDate = endDate == null ? new Date() : endDate; + this.archiveOwner = archiveOwner; + this.with = with; + this.query = null; + } + + /** + * Creates a query for messages from a message archive. + * + * @param startDate Start (inclusive) of period for which to return messages. EPOCH will be used if no value is provided. + * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. + * @param archiveOwner The message archive owner. + * @param with An optional conversation partner + * @param query A search string to be used for text-based search. + */ + public AbstractPaginatedMamQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final JID archiveOwner, @Nullable final JID with, @Nonnull final String query) + { + this.startDate = startDate == null ? new Date( 0L ) : startDate ; + this.endDate = endDate == null ? new Date() : endDate; + this.archiveOwner = archiveOwner; + this.with = with; + this.query = query; + } + + /** + * Get a page of a potentially larger list of archived messages that are the result of this query. + * + * @param after an optional message identifier that acts as a starting point (exclusive) of the messages to be returned. + * @param before an optional message identifier that acts as an end point (exclusive) of the messages to be returned. + * @param maxResults The maximum number of archived messages to return + * @param isPagingBackwards true if the order of the messages is from new to old, otherwise false. + * @return A list of archived messages + * @throws DataRetrievalException On any problem that occurs while retrieving the page of archived messages. + */ + abstract protected List getPage(@Nullable final Long after, @Nullable final Long before, final int maxResults, final boolean isPagingBackwards) throws DataRetrievalException; + + /** + * Returns the amount of messages that are in the entire, unlimited/unpaged, result set. + *

+ * The returned number is allowed to be an approximation. + * + * @return A message count, or -1 if unavailable. + */ + abstract protected int getTotalCount(); + + @Nonnull + public Date getStartDate() + { + return startDate; + } + + @Nonnull + public Date getEndDate() + { + return endDate; + } + + @Nonnull + public JID getArchiveOwner() + { + return archiveOwner; + } + + @Nullable + public JID getWith() + { + return with; + } + + @Nullable + public String getQuery() { + return query; + } + + @Override + public String toString() + { + return "AbstractPaginatedMamQuery{" + + "startDate=" + startDate + + ", endDate=" + endDate + + ", archiveOwner=" + archiveOwner + + ", with=" + with + + ", query='" + query + '\'' + + '}'; + } +} diff --git a/src/java/com/reucon/openfire/plugin/archive/impl/MucMamPersistenceManager.java b/src/java/com/reucon/openfire/plugin/archive/impl/MucMamPersistenceManager.java index c4e5c7104..a2b051406 100644 --- a/src/java/com/reucon/openfire/plugin/archive/impl/MucMamPersistenceManager.java +++ b/src/java/com/reucon/openfire/plugin/archive/impl/MucMamPersistenceManager.java @@ -116,7 +116,7 @@ public Collection findMessages(Date startDate, Date endDate, JI } } else { Log.debug("Using Monitoring plugin tables"); - final PaginatedMucMessageDatabaseQuery paginatedMessageDatabaseQuery = new PaginatedMucMessageDatabaseQuery(startDate, endDate, room.getJID(), messageOwner, with); + final PaginatedMucMessageDatabaseQuery paginatedMessageDatabaseQuery = new PaginatedMucMessageDatabaseQuery(startDate, endDate, room, messageOwner, with); Log.debug("Request for message archive of room '{}' resulted in the following query data: {}", room.getJID(), paginatedMessageDatabaseQuery); totalCount = paginatedMessageDatabaseQuery.getTotalCount(); if (totalCount == 0) { @@ -174,7 +174,7 @@ public Collection findMessages(Date startDate, Date endDate, JI final PaginatedMucMessageFromOpenfireDatabaseQuery paginatedMucMessageFromOpenfireDatabaseQuery = new PaginatedMucMessageFromOpenfireDatabaseQuery(startDate, endDate, room, with); nextPage = paginatedMucMessageFromOpenfireDatabaseQuery.getPage(afterForNextPage, beforeForNextPage, 1, isPagingBackwards); } else { - final PaginatedMucMessageDatabaseQuery paginatedMessageDatabaseQuery = new PaginatedMucMessageDatabaseQuery(startDate, endDate, room.getJID(), messageOwner, with); + final PaginatedMucMessageDatabaseQuery paginatedMessageDatabaseQuery = new PaginatedMucMessageDatabaseQuery(startDate, endDate, room, messageOwner, with); nextPage = paginatedMessageDatabaseQuery.getPage(afterForNextPage, beforeForNextPage, 1, isPagingBackwards); } } diff --git a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMessageDatabaseQuery.java b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMessageDatabaseQuery.java index 146dc4dfd..6dcd45261 100644 --- a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMessageDatabaseQuery.java +++ b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMessageDatabaseQuery.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Ignite Realtime Foundation. All rights reserved. + * Copyright (C) 2020-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 @@ -39,60 +39,21 @@ * * @author Guus der Kinderen, guus.der.kinderen@gmail.com */ -public class PaginatedMessageDatabaseQuery +public class PaginatedMessageDatabaseQuery extends AbstractPaginatedMamQuery { - private static final Logger Log = LoggerFactory.getLogger(PaginatedMessageDatabaseQuery.class ); - - @Nonnull - private final Date startDate; - - @Nonnull - private final Date endDate; - - @Nonnull - private final JID owner; - - @Nullable - private final JID with; + private static final Logger Log = LoggerFactory.getLogger(PaginatedMessageDatabaseQuery.class); /** * Creates a query for messages from a message archive. * - * @param startDate Start (inclusive) of period for which to return messages. EPOCH will be used if no value is provided. - * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. - * @param owner The message archive owner. - * @param with An optional conversation partner + * @param startDate Start (inclusive) of period for which to return messages. EPOCH will be used if no value is provided. + * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. + * @param archiveOwner The message archive owner. + * @param with An optional conversation partner */ - public PaginatedMessageDatabaseQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final JID owner, @Nullable final JID with) - { - this.startDate = startDate == null ? new Date( 0L ) : startDate ; - this.endDate = endDate == null ? new Date() : endDate; - this.owner = owner; - this.with = with; - } - - @Nonnull - public Date getStartDate() - { - return startDate; - } - - @Nonnull - public Date getEndDate() + public PaginatedMessageDatabaseQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final JID archiveOwner, @Nullable final JID with) { - return endDate; - } - - @Nonnull - public JID getOwner() - { - return owner; - } - - @Nullable - public JID getWith() - { - return with; + super(startDate, endDate, archiveOwner, with); } @Override @@ -101,12 +62,13 @@ public String toString() return "PaginatedMessageQuery{" + "startDate=" + startDate + ", endDate=" + endDate + - ", owner=" + owner + + ", archiveOwner=" + archiveOwner + ", with='" + with + '\'' + '}'; } - protected List getPage( @Nullable final Long after, @Nullable final Long before, final int maxResults, final boolean isPagingBackwards ) throws DataRetrievalException { + @Override + protected List getPage(@Nullable final Long after, @Nullable final Long before, final int maxResults, final boolean isPagingBackwards) throws DataRetrievalException { Log.trace( "Getting page of archived messages. After: {}, Before: {}, Max results: {}, Paging backwards: {}", after, before, maxResults, isPagingBackwards ); final List archivedMessages = new ArrayList<>(); @@ -128,9 +90,9 @@ protected List getPage( @Nullable final Long after, @Nullable f pstmt.setLong( 1, dateToMillis( startDate ) ); pstmt.setLong( 2, dateToMillis( endDate ) ); - pstmt.setString( 3, owner.toBareJID() ); - pstmt.setString( 4, owner.toBareJID() ); - pstmt.setString( 5, owner.toBareJID() ); + pstmt.setString( 3, archiveOwner.toBareJID() ); + pstmt.setString( 4, archiveOwner.toBareJID() ); + pstmt.setString( 5, archiveOwner.toBareJID() ); int pos = 5; if ( with != null ) { @@ -156,19 +118,19 @@ protected List getPage( @Nullable final Long after, @Nullable f Log.trace( "Constructed query: {}", query ); rs = pstmt.executeQuery(); while (rs.next()) { - final ArchivedMessage archivedMessage = JdbcPersistenceManager.extractMessage(owner, rs); + final ArchivedMessage archivedMessage = JdbcPersistenceManager.extractMessage(archiveOwner, rs); archivedMessages.add(archivedMessage); } if(isPagingBackwards){ Collections.reverse(archivedMessages); } } catch (SQLException e) { - Log.error("SQL failure during MAM for owner: {}", this.owner, e); + Log.error("SQL failure during MAM for owner: {}", this.archiveOwner, e); if (!IQQueryHandler.IGNORE_RETRIEVAL_EXCEPTIONS.getValue()) { throw new DataRetrievalException(e); } } catch (DocumentException e) { - Log.error("Unable to parse 'stanza' value as valid XMPP for owner {}", this.owner, e); + Log.error("Unable to parse 'stanza' value as valid XMPP for owner {}", this.archiveOwner, e); } finally { DbConnectionManager.closeConnection(rs, pstmt, connection); } @@ -180,6 +142,7 @@ private Long dateToMillis(@Nullable final Date date) { return date == null ? null : date.getTime(); } + @Override protected int getTotalCount() { Connection connection = null; @@ -192,9 +155,9 @@ protected int getTotalCount() pstmt = connection.prepareStatement( buildQueryForTotalCount() ); pstmt.setLong( 1, dateToMillis( startDate ) ); pstmt.setLong( 2, dateToMillis( endDate ) ); - pstmt.setString( 3, owner.toBareJID() ); - pstmt.setString( 4, owner.toBareJID() ); - pstmt.setString( 5, owner.toBareJID() ); + pstmt.setString( 3, archiveOwner.toBareJID() ); + pstmt.setString( 4, archiveOwner.toBareJID() ); + pstmt.setString( 5, archiveOwner.toBareJID() ); int pos = 5; if ( with != null ) { diff --git a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMessageLuceneQuery.java b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMessageLuceneQuery.java index cddc89eba..bb81ca5cc 100644 --- a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMessageLuceneQuery.java +++ b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMessageLuceneQuery.java @@ -16,29 +16,36 @@ import org.slf4j.LoggerFactory; import org.xmpp.packet.JID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; -public class PaginatedMessageLuceneQuery +/** + * Encapsulates responsibility of creating a Lucene query that retrieves a specific subset (page) of archived messages + * from a specific end-user entity owner (the archive that's queried is considered to be a 'personal archive'). + * + * @author Guus der Kinderen, guus.der.kinderen@gmail.com + */ +public class PaginatedMessageLuceneQuery extends AbstractPaginatedMamQuery { private static final Logger Log = LoggerFactory.getLogger( PaginatedMessageLuceneQuery.class ); - private final Date startDate; - private final Date endDate; - private final JID owner; - private final JID with; - private final String query; - - public PaginatedMessageLuceneQuery( final Date startDate, final Date endDate, final JID owner, final JID with, final String query ) + /** + * Creates a query for messages from a message archive. + * + * @param startDate Start (inclusive) of period for which to return messages. EPOCH will be used if no value is provided. + * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. + * @param archiveOwner The message archive owner. + * @param with An optional conversation partner + * @param query A search string to be used for text-based search. + */ + public PaginatedMessageLuceneQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final JID archiveOwner, @Nullable final JID with, @Nonnull final String query) { - this.startDate = startDate == null ? new Date( 0L ) : startDate ; - this.endDate = endDate == null ? new Date() : endDate; - this.owner = owner; - this.with = with; - this.query = query; + super(startDate, endDate, archiveOwner, with, query); } protected IndexSearcher getSearcher() throws IOException @@ -49,8 +56,8 @@ protected IndexSearcher getSearcher() throws IOException return searcher; } - - public List getPage( final Long after, final Long before, final int maxResults, final boolean isPagingBackwards ) throws DataRetrievalException { + @Override + public List getPage(final Long after, final Long before, final int maxResults, final boolean isPagingBackwards) throws DataRetrievalException { Log.debug( "Retrieving archived messages page. After: {}, Before: {}, maxResults: {}, isPagingBackwards: {}", after, before, maxResults, isPagingBackwards); final List result = new ArrayList<>(); try @@ -63,7 +70,7 @@ public List getPage( final Long after, final Long before, final { final Document doc = searcher.doc(scoreDoc.doc); final long messageID = Long.parseLong(doc.get("messageID")); - final ArchivedMessage archivedMessage = JdbcPersistenceManager.getArchivedMessage(messageID, owner); + final ArchivedMessage archivedMessage = JdbcPersistenceManager.getArchivedMessage(messageID, archiveOwner); if ( archivedMessage != null ) { result.add( archivedMessage ); } @@ -75,7 +82,7 @@ public List getPage( final Long after, final Long before, final } } catch ( Exception e ) { - Log.warn( "An exception occurred while trying to query the Lucene index to get messages from archive of owner {}.", owner, e ); + Log.warn( "An exception occurred while trying to query the Lucene index to get messages from archive of owner {}.", archiveOwner, e ); if (!IQQueryHandler.IGNORE_RETRIEVAL_EXCEPTIONS.getValue()) { throw new DataRetrievalException(e); } @@ -89,6 +96,7 @@ public List getPage( final Long after, final Long before, final * * @return A message count, or -1 if unavailable. */ + @Override public int getTotalCount() { try { @@ -103,7 +111,7 @@ public int getTotalCount() { } catch ( Exception e ) { - Log.warn( "An exception occurred while trying to get a count of messages that match a query for message from archive of owner {}.", owner, e ); + Log.warn( "An exception occurred while trying to get a count of messages that match a query for message from archive of owner {}.", archiveOwner, e ); return -1; } } @@ -119,7 +127,7 @@ protected Query getLuceneQueryForAllResults() throws ParseException builder.add(textQuery, BooleanClause.Occur.MUST ); // Limit to the owner of the archive. - builder.add(new TermQuery(new Term("owner", owner.toBareJID() ) ), BooleanClause.Occur.MUST ); + builder.add(new TermQuery(new Term("owner", archiveOwner.toBareJID() ) ), BooleanClause.Occur.MUST ); // Limit potential results to the requested time range. Note that these values are always non-null in this method (might be 'EPOCH' though). final Query dateRangeQuery = NumericDocValuesField.newSlowRangeQuery("sentDate", startDate.getTime(), endDate.getTime()); @@ -170,7 +178,7 @@ public String toString() return "PaginatedMessageLuceneQuery{" + "startDate=" + startDate + ", endDate=" + endDate + - ", owner=" + owner + + ", owner=" + archiveOwner + ", with=" + with + ", query='" + query + '\'' + '}'; diff --git a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageDatabaseQuery.java b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageDatabaseQuery.java index 9a86dbd94..f0310f085 100644 --- a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageDatabaseQuery.java +++ b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageDatabaseQuery.java @@ -18,6 +18,7 @@ import com.reucon.openfire.plugin.archive.xep0313.IQQueryHandler; import org.dom4j.DocumentException; import org.jivesoftware.database.DbConnectionManager; +import org.jivesoftware.openfire.muc.MUCRoom; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xmpp.packet.JID; @@ -39,29 +40,14 @@ * * @author Guus der Kinderen, guus.der.kinderen@gmail.com */ -public class PaginatedMucMessageDatabaseQuery +public class PaginatedMucMessageDatabaseQuery extends AbstractPaginatedMamMucQuery { - private static final Logger Log = LoggerFactory.getLogger(PaginatedMucMessageDatabaseQuery.class ); - - @Nonnull - private final Date startDate; - - @Nonnull - private final Date endDate; - - @Nonnull - private final JID archiveOwner; - - @Nonnull - private final JID messageOwner; - - @Nullable - private final JID with; + private static final Logger Log = LoggerFactory.getLogger(PaginatedMucMessageDatabaseQuery.class); /** - * Creates a query for messages from a message archive. + * Creates a query for messages from a message archive of a multi-user chat room. * - * Two identifing JIDs are provided to this method: one is the JID of the room that's being queried (the 'archive + * Two identifying JIDs are provided to this method: one is the JID of the room that's being queried (the 'archive * owner'). To be able to return the private messages for a particular user from the room archive, an additional JID * is provided, that identifies the user for which to retrieve the messages. * @@ -69,45 +55,11 @@ public class PaginatedMucMessageDatabaseQuery * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. * @param archiveOwner The message archive owner (the JID of the chat room). * @param messageOwner The entity for which to return messages (typically the JID of the entity making the request). - * @param with An optional converstation partner (or message author, in case of MUC). + * @param with An optional conversation partner (or message author, in case of MUC). */ - public PaginatedMucMessageDatabaseQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final JID archiveOwner, @Nonnull final JID messageOwner, @Nullable final JID with) - { - this.startDate = startDate == null ? new Date( 0L ) : startDate ; - this.endDate = endDate == null ? new Date() : endDate; - this.archiveOwner = archiveOwner; - this.messageOwner = messageOwner; - this.with = with; - } - - @Nonnull - public Date getStartDate() - { - return startDate; - } - - @Nonnull - public Date getEndDate() + public PaginatedMucMessageDatabaseQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final MUCRoom archiveOwner, @Nonnull final JID messageOwner, @Nullable final JID with) { - return endDate; - } - - @Nonnull - public JID getArchiveOwner() - { - return archiveOwner; - } - - @Nonnull - public JID getMessageOwner() - { - return messageOwner; - } - - @Nullable - public JID getWith() - { - return with; + super(startDate, endDate, archiveOwner, messageOwner, with); } @Override @@ -122,7 +74,8 @@ public String toString() '}'; } - protected List getPage( @Nullable final Long after, @Nullable final Long before, final int maxResults, final boolean isPagingBackwards ) throws DataRetrievalException { + @Override + protected List getPage(@Nullable final Long after, @Nullable final Long before, final int maxResults, final boolean isPagingBackwards) throws DataRetrievalException { Log.trace( "Getting page of archived messages. After: {}, Before: {}, Max results: {}, Paging backwards: {}", after, before, maxResults, isPagingBackwards ); final List archivedMessages = new ArrayList<>(); @@ -196,6 +149,7 @@ private Long dateToMillis(@Nullable final Date date) { return date == null ? null : date.getTime(); } + @Override protected int getTotalCount() { Connection connection = null; diff --git a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageFromOpenfireDatabaseQuery.java b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageFromOpenfireDatabaseQuery.java index a5932b3ff..f2fcc8173 100644 --- a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageFromOpenfireDatabaseQuery.java +++ b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageFromOpenfireDatabaseQuery.java @@ -24,6 +24,8 @@ import org.slf4j.LoggerFactory; import org.xmpp.packet.JID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -33,48 +35,29 @@ import java.util.List; /** - * Encapsulates responsibility of creating a database query that retrieves a specific subset (page) of archived messages related to a MUC room. + * Encapsulates responsibility of creating a database query that retrieves a specific subset (page) of archived messages + * related to a MUC room. * * Note that this implementation primarily makes use of the database tables that are provided by Openfire (core), and * not of the database tables that are provided by the Monitoring plugin. * * @author Guus der Kinderen, guus.der.kinderen@gmail.com */ -public class PaginatedMucMessageFromOpenfireDatabaseQuery +public class PaginatedMucMessageFromOpenfireDatabaseQuery extends AbstractPaginatedMamMucQuery { - private static final Logger Log = LoggerFactory.getLogger(PaginatedMucMessageFromOpenfireDatabaseQuery.class ); + private static final Logger Log = LoggerFactory.getLogger(PaginatedMucMessageFromOpenfireDatabaseQuery.class); - private final Date startDate; - private final Date endDate; - private final MUCRoom room; - private final JID with; - - public PaginatedMucMessageFromOpenfireDatabaseQuery(Date startDate, Date endDate, MUCRoom room, JID with ) - { - this.startDate = startDate == null ? new Date( 0L ) : startDate ; - this.endDate = endDate == null ? new Date() : endDate; - this.room = room; - this.with = with; - } - - public Date getStartDate() - { - return startDate; - } - - public Date getEndDate() - { - return endDate; - } - - public MUCRoom getRoom() - { - return room; - } - - public JID getWith() + /** + * Creates a query for messages from a message archive belonging to a multi-user chat room. + * + * @param startDate Start (inclusive) of period for which to return messages. EPOCH will be used if no value is provided. + * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. + * @param room The message archive owner (the chat room). + * @param with An optional conversation partner (or message author, in case of MUC). + */ + public PaginatedMucMessageFromOpenfireDatabaseQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final MUCRoom room, @Nullable final JID with) { - return with; + super(startDate, endDate, room, null, with); } @Override @@ -88,7 +71,8 @@ public String toString() '}'; } - protected List getPage( final Long after, final Long before, final int maxResults, final boolean isPagingBackwards ) throws DataRetrievalException { + @Override + protected List getPage(final Long after, final Long before, final int maxResults, final boolean isPagingBackwards) throws DataRetrievalException { Log.trace( "Getting page of archived messages. After: {}, Before: {}, Max results: {}, Paging backwards: {}", after, before, maxResults, isPagingBackwards ); final List msgs = new LinkedList<>(); @@ -152,7 +136,7 @@ protected List getPage( final Long after, final Long before, fi return msgs; } - + @Override protected int getTotalCount() { Connection connection = null; diff --git a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageFromOpenfireLuceneQuery.java b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageFromOpenfireLuceneQuery.java index 236d0b4a9..5e4b0d315 100644 --- a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageFromOpenfireLuceneQuery.java +++ b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageFromOpenfireLuceneQuery.java @@ -19,26 +19,31 @@ import org.slf4j.LoggerFactory; import org.xmpp.packet.JID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.IOException; import java.util.*; -public class PaginatedMucMessageFromOpenfireLuceneQuery +public class PaginatedMucMessageFromOpenfireLuceneQuery extends AbstractPaginatedMamMucQuery { - private static final Logger Log = LoggerFactory.getLogger(PaginatedMucMessageFromOpenfireLuceneQuery.class ); + private static final Logger Log = LoggerFactory.getLogger(PaginatedMucMessageFromOpenfireLuceneQuery.class); - private final Date startDate; - private final Date endDate; - private final MUCRoom room; - private final JID sender; - private final String query; - - public PaginatedMucMessageFromOpenfireLuceneQuery(final Date startDate, final Date endDate, final MUCRoom room, final JID sender, final String query ) + /** + * Creates a query for messages from a message archive of a multi-user chat room. + * + * Two identifying JIDs are provided to this method: one is the JID of the room that's being queried (the 'archive + * owner'). To be able to return the private messages for a particular user from the room archive, an additional JID + * is provided, that identifies the user for which to retrieve the messages. + * + * @param startDate Start (inclusive) of period for which to return messages. EPOCH will be used if no value is provided. + * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. + * @param room The message archive owner (the chat room). + * @param sender The entity for which to return messages (typically the JID of the entity making the request). + * @param query A search string to be used for text-based search. + */ + public PaginatedMucMessageFromOpenfireLuceneQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final MUCRoom room, @Nullable final JID sender, @Nonnull final String query) { - this.startDate = startDate == null ? new Date( 0L ) : startDate ; - this.endDate = endDate == null ? new Date() : endDate; - this.room = room; - this.sender = sender; - this.query = query; + super(startDate, endDate, room, sender, null, query); } protected IndexSearcher getSearcher() throws IOException @@ -52,7 +57,8 @@ protected IndexSearcher getSearcher() throws IOException return searcher; } - public List getPage( final Long after, final Long before, final int maxResults, final boolean isPagingBackwards ) throws DataRetrievalException { + @Override + public List getPage(final Long after, final Long before, final int maxResults, final boolean isPagingBackwards) throws DataRetrievalException { Log.debug( "Retrieving archived messages page. After: {}, Before: {}, maxResults: {}, isPagingBackwards: {}", after, before, maxResults, isPagingBackwards); final List result = new ArrayList<>(); try @@ -91,6 +97,7 @@ public List getPage( final Long after, final Long before, final * * @return A message count, or -1 if unavailable. */ + @Override public int getTotalCount() { try { @@ -128,13 +135,13 @@ protected Query getLuceneQueryForAllResults() throws ParseException builder.add(dateRangeQuery, BooleanClause.Occur.MUST); // If defined, limit to specific senders. - if ( sender != null ) { + if ( messageOwner != null ) { // Always limit to the bare JID of the sender. - builder.add(new TermQuery(new Term("senderBare", sender.toBareJID() ) ), BooleanClause.Occur.MUST ); + builder.add(new TermQuery(new Term("senderBare", messageOwner.toBareJID() ) ), BooleanClause.Occur.MUST ); // If the query specified a more specific full JID, include the resource part in the filter too. - if ( sender.getResource() != null ) { - builder.add(new TermQuery( new Term( "senderResource", sender.getResource() ) ), BooleanClause.Occur.MUST ); + if ( messageOwner.getResource() != null ) { + builder.add(new TermQuery( new Term( "senderResource", messageOwner.getResource() ) ), BooleanClause.Occur.MUST ); } } @@ -173,7 +180,7 @@ public String toString() "startDate=" + startDate + ", endDate=" + endDate + ", room=" + room + - ", sender=" + sender + + ", sender=" + messageOwner + ", query='" + query + '\'' + '}'; } diff --git a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageLuceneQuery.java b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageLuceneQuery.java index a32ea4383..c97b5a9ee 100644 --- a/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageLuceneQuery.java +++ b/src/java/com/reucon/openfire/plugin/archive/impl/PaginatedMucMessageLuceneQuery.java @@ -17,31 +17,30 @@ import org.slf4j.LoggerFactory; import org.xmpp.packet.JID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; -public class PaginatedMucMessageLuceneQuery +public class PaginatedMucMessageLuceneQuery extends AbstractPaginatedMamMucQuery { private static final Logger Log = LoggerFactory.getLogger( PaginatedMucMessageLuceneQuery.class ); - private final Date startDate; - private final Date endDate; - private final MUCRoom owner; - private final JID messageOwner; - private final JID with; - private final String query; - - public PaginatedMucMessageLuceneQuery(final Date startDate, final Date endDate, final MUCRoom owner, final JID messageOwner, final JID with, final String query ) + /** + * Creates a query for messages from a message archive of a multi-user chat room. + * + * @param startDate Start (inclusive) of period for which to return messages. EPOCH will be used if no value is provided. + * @param endDate End (inclusive) of period for which to return messages. 'now' will be used if no value is provided. + * @param owner The message archive owner (the chat room). + * @param messageOwner The entity for which to return messages (typically the JID of the entity making the request). + * @param query A search string to be used for text-based search. + */ + public PaginatedMucMessageLuceneQuery(@Nullable final Date startDate, @Nullable final Date endDate, @Nonnull final MUCRoom owner, final JID messageOwner, @Nullable final JID with, @Nonnull final String query) { - this.startDate = startDate == null ? new Date( 0L ) : startDate ; - this.endDate = endDate == null ? new Date() : endDate; - this.owner = owner; - this.messageOwner = messageOwner; - this.with = with; - this.query = query; + super(startDate, endDate, owner, messageOwner, with, query); } protected IndexSearcher getSearcher() throws IOException @@ -52,8 +51,8 @@ protected IndexSearcher getSearcher() throws IOException return searcher; } - - public List getPage( final Long after, final Long before, final int maxResults, final boolean isPagingBackwards ) throws DataRetrievalException { + @Override + public List getPage(final Long after, final Long before, final int maxResults, final boolean isPagingBackwards) throws DataRetrievalException { Log.debug( "Retrieving archived messages page. After: {}, Before: {}, maxResults: {}, isPagingBackwards: {}", after, before, maxResults, isPagingBackwards); final List result = new ArrayList<>(); try @@ -71,7 +70,7 @@ public List getPage( final Long after, final Long before, final final Document doc = searcher.doc(scoreDoc.doc); final long messageID = Long.parseLong(doc.get("messageID")); Log.debug("message ID: {}", messageID); - final ArchivedMessage archivedMessage = MucMamPersistenceManager.getArchivedMessage(messageID, owner); + final ArchivedMessage archivedMessage = MucMamPersistenceManager.getArchivedMessage(messageID, room); if ( archivedMessage != null ) { result.add( archivedMessage ); } @@ -85,7 +84,7 @@ public List getPage( final Long after, final Long before, final } } catch ( Exception e ) { - Log.warn( "An exception occurred while trying to query the Lucene index to get messages from archive of room {}.", owner.getJID(), e ); + Log.warn( "An exception occurred while trying to query the Lucene index to get messages from archive of room {}.", room.getJID(), e ); if (!IQQueryHandler.IGNORE_RETRIEVAL_EXCEPTIONS.getValue()) { throw new DataRetrievalException(e); } @@ -99,6 +98,7 @@ public List getPage( final Long after, final Long before, final * * @return A message count, or -1 if unavailable. */ + @Override public int getTotalCount() { try { @@ -113,7 +113,7 @@ public int getTotalCount() { } catch ( Exception e ) { - Log.warn( "An exception occurred while trying to get a count of messages that match a query for message from archive of room {}.", owner.getJID(), e ); + Log.warn( "An exception occurred while trying to get a count of messages that match a query for message from archive of room {}.", room.getJID(), e ); return -1; } } @@ -132,7 +132,7 @@ protected Query getLuceneQueryForAllResults() throws ParseException // - look up messages from the archive of the MUC that are not private // - look up messages from the archive of the MUC that are private, where user making the request is a sender or recipient. final BooleanQuery ownerFilter = new BooleanQuery.Builder() - .add(new TermQuery(new Term("room", owner.getJID().toBareJID() ) ), BooleanClause.Occur.MUST ) // room + .add(new TermQuery(new Term("room", room.getJID().toBareJID() ) ), BooleanClause.Occur.MUST ) // room .setMinimumNumberShouldMatch(1) // Either non-private messages... @@ -196,7 +196,7 @@ public String toString() return "PaginatedMessageLuceneQuery{" + "startDate=" + startDate + ", endDate=" + endDate + - ", room=" + owner.getJID() + + ", room=" + room.getJID() + ", with=" + with + ", query='" + query + '\'' + '}';