diff --git a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveReaderWorkItem.java b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveReaderWorkItem.java index fb9e9d142..35d29ef98 100644 --- a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveReaderWorkItem.java +++ b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveReaderWorkItem.java @@ -23,7 +23,7 @@ package org.openjdk.skara.bots.mlbridge; import org.openjdk.skara.bot.WorkItem; -import org.openjdk.skara.mailinglist.MailingList; +import org.openjdk.skara.mailinglist.MailingListReader; import java.nio.file.Path; import java.time.Duration; @@ -31,9 +31,9 @@ public class ArchiveReaderWorkItem implements WorkItem { private final MailingListArchiveReaderBot bot; - private final MailingList list; + private final MailingListReader list; - ArchiveReaderWorkItem(MailingListArchiveReaderBot bot, MailingList list) { + ArchiveReaderWorkItem(MailingListArchiveReaderBot bot, MailingListReader list) { this.bot = bot; this.list = list; } diff --git a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveWorkItem.java b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveWorkItem.java index d628ffd43..2b9ef18c6 100644 --- a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveWorkItem.java +++ b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveWorkItem.java @@ -171,7 +171,7 @@ private void updateWebrevComment(List comments, int index, List parseArchive(MailingList archive) { + private List parseArchive(MailingListReader archive) { var conversations = archive.conversations(Duration.ofDays(365)); if (conversations.size() == 0) { @@ -256,7 +256,7 @@ public Collection run(Path scratchPath) { var archiveRepo = materializeArchive(path); var mboxBasePath = path.resolve(bot.codeRepo().name()); var mbox = MailingListServerFactory.createMboxFileServer(mboxBasePath); - var reviewArchiveList = mbox.getList(pr.id()); + var reviewArchiveList = mbox.getListReader(pr.id()); var sentMails = parseArchive(reviewArchiveList); var labels = new HashSet<>(pr.labelNames()); @@ -334,8 +334,6 @@ public Collection run(Path scratchPath) { var webrevPath = scratchPath.resolve("mlbridge-webrevs"); var listServer = MailingListServerFactory.createMailmanServer(bot.listArchive(), bot.smtpServer(), bot.sendInterval()); - var list = listServer.getList(recipients.get(0).toString()); - var archiver = new ReviewArchive(pr, bot.emailAddress()); // Regular comments @@ -382,7 +380,12 @@ public Collection run(Path scratchPath) { } // Push all new mails to the archive repository - newMails.forEach(reviewArchiveList::post); + for (var newMail : newMails) { + var forArchiving = Email.from(newMail) + .recipient(EmailAddress.from(pr.id() + "@mbox")) + .build(); + mbox.post(forArchiving); + } pushMbox(archiveRepo, "Adding comments for PR " + bot.codeRepo().name() + "/" + pr.id()); // Finally post all new mails to the actual list @@ -396,7 +399,7 @@ public Collection run(Path scratchPath) { .headers(bot.headers()) .recipients(recipients) .build(); - list.post(filteredEmail); + listServer.post(filteredEmail); } } catch (IOException e) { throw new UncheckedIOException(e); diff --git a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListArchiveReaderBot.java b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListArchiveReaderBot.java index 46e544d71..74154f6bf 100644 --- a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListArchiveReaderBot.java +++ b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListArchiveReaderBot.java @@ -36,7 +36,7 @@ public class MailingListArchiveReaderBot implements Bot { private final EmailAddress archivePoster; - private final Set lists; + private final MailingListReader list; private final Set repositories; private final Map parsedConversations = new HashMap<>(); private final Map resolvedPullRequests = new HashMap<>(); @@ -45,9 +45,9 @@ public class MailingListArchiveReaderBot implements Bot { private final Pattern pullRequestLinkPattern = Pattern.compile("^(?:PR: |Pull request:\\R)(.*?)$", Pattern.MULTILINE); private final Logger log = Logger.getLogger("org.openjdk.skara.bots.mlbridge"); - MailingListArchiveReaderBot(EmailAddress archivePoster, Set lists, Set repositories) { + MailingListArchiveReaderBot(EmailAddress archivePoster, MailingListReader list, Set repositories) { this.archivePoster = archivePoster; - this.lists = lists; + this.list = list; this.repositories = repositories; } @@ -131,11 +131,9 @@ synchronized void inspect(Conversation conversation) { @Override public List getPeriodicItems() { - var readerItems = lists.stream() - .map(list -> new ArchiveReaderWorkItem(this, list)) - .collect(Collectors.toList()); - - var ret = new ArrayList(readerItems); + var readerItems = new ArchiveReaderWorkItem(this, list); + var ret = new ArrayList(); + ret.add(readerItems); // Check if there are any potential new comments to post var item = commentQueue.poll(); diff --git a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBotFactory.java b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBotFactory.java index 0f82802b2..ea790a68b 100644 --- a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBotFactory.java +++ b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBotFactory.java @@ -88,7 +88,6 @@ public List create(BotConfiguration configuration) { var archiveRef = configuration.repositoryRef(specific.get("archive").asString()); var issueTracker = URIBuilder.base(specific.get("issues").asString()).build(); - var listNamesForReading = new HashSet(); var allRepositories = new HashSet(); var readyLabels = specific.get("ready").get("labels").stream() @@ -99,6 +98,7 @@ public List create(BotConfiguration configuration) { .collect(Collectors.toMap(obj -> obj.get("user").asString(), obj -> Pattern.compile(obj.get("pattern").asString()))); var cooldown = specific.contains("cooldown") ? Duration.parse(specific.get("cooldown").asString()) : Duration.ofMinutes(1); + var mailmanServer = MailingListServerFactory.createMailmanServer(listArchive, listSmtp, Duration.ZERO); for (var repoConfig : specific.get("repositories").asArray()) { var repo = repoConfig.get("repository").asString(); @@ -109,7 +109,20 @@ public List create(BotConfiguration configuration) { repoConfig.get("headers").fields().stream() .collect(Collectors.toMap(JSONObject.Field::name, field -> field.value().asString())) : Map.of(); + var lists = parseLists(repoConfig.get("lists")); + if (!repoConfig.contains("bidirectional") || repoConfig.get("bidirectional").asBoolean()) { + var listNamesForReading = new HashSet(); + for (var list : lists) { + listNamesForReading.add(list.list()); + } + var listsForReading = listNamesForReading.stream() + .map(EmailAddress::localPart) + .collect(Collectors.toList()); + var bot = new MailingListArchiveReaderBot(from, mailmanServer.getListReader(listsForReading.toArray(new String[0])), allRepositories); + ret.add(bot); + } + var folder = repoConfig.contains("folder") ? repoConfig.get("folder").asString() : configuration.repositoryName(repo); var webrevGenerateHTML = true; @@ -156,22 +169,9 @@ public List create(BotConfiguration configuration) { } ret.add(botBuilder.build()); - if (!repoConfig.contains("bidirectional") || repoConfig.get("bidirectional").asBoolean()) { - for (var list : lists) { - listNamesForReading.add(list.list()); - } - } allRepositories.add(configuration.repository(repo)); } - var mailmanServer = MailingListServerFactory.createMailmanServer(listArchive, listSmtp, Duration.ZERO); - var listsForReading = listNamesForReading.stream() - .map(name -> mailmanServer.getList(name.toString())) - .collect(Collectors.toSet()); - - var bot = new MailingListArchiveReaderBot(from, listsForReading, allRepositories); - ret.add(bot); - return ret; } } diff --git a/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/MailingListArchiveReaderBotTests.java b/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/MailingListArchiveReaderBotTests.java index ed3c98c19..a9947398f 100644 --- a/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/MailingListArchiveReaderBotTests.java +++ b/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/MailingListArchiveReaderBotTests.java @@ -38,7 +38,7 @@ import static org.junit.jupiter.api.Assertions.*; class MailingListArchiveReaderBotTests { - private void addReply(Conversation conversation, EmailAddress recipient, MailingList mailingList, PullRequest pr, String reply) { + private void addReply(Conversation conversation, EmailAddress recipient, MailingListServer mailingListServer, PullRequest pr, String reply) { var first = conversation.first(); var references = first.id().toString(); var email = Email.create(EmailAddress.from("Commenter", "c@test.test"), "Re: RFR: " + pr.title(), reply) @@ -47,11 +47,11 @@ private void addReply(Conversation conversation, EmailAddress recipient, Mailing .header("In-Reply-To", first.id().toString()) .header("References", references) .build(); - mailingList.post(email); + mailingListServer.post(email); } - private void addReply(Conversation conversation, EmailAddress recipient, MailingList mailingList, PullRequest pr) { - addReply(conversation, recipient, mailingList, pr, "Looks good"); + private void addReply(Conversation conversation, EmailAddress recipient, MailingListServer mailingListServer, PullRequest pr) { + addReply(conversation, recipient, mailingListServer, pr, "Looks good"); } @Test @@ -86,8 +86,8 @@ void simpleArchive(TestInfo testInfo) throws IOException { // The mailing list as well var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); - var readerBot = new MailingListArchiveReaderBot(from, Set.of(mailmanList), Set.of(archive)); + var mailmanList = mailmanServer.getListReader(listAddress.address()); + var readerBot = new MailingListArchiveReaderBot(from, mailmanList, Set.of(archive)); // Populate the projects repository var localRepo = CheckableRepository.init(tempFolder.path(), author.repositoryType()); @@ -113,7 +113,7 @@ void simpleArchive(TestInfo testInfo) throws IOException { // Post a reply directly to the list var conversations = mailmanList.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); - addReply(conversations.get(0), listAddress, mailmanList, pr); + addReply(conversations.get(0), listAddress, mailmanServer, pr); listServer.processIncoming(); // Another archive reader pass - has to be done twice @@ -162,8 +162,8 @@ void rememberBridged(TestInfo testInfo) throws IOException { // The mailing list as well var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); - var readerBot = new MailingListArchiveReaderBot(from, Set.of(mailmanList), Set.of(archive)); + var mailmanList = mailmanServer.getListReader(listAddress.address()); + var readerBot = new MailingListArchiveReaderBot(from, mailmanList, Set.of(archive)); // Populate the projects repository var localRepo = CheckableRepository.init(tempFolder.path(), author.repositoryType()); @@ -185,7 +185,7 @@ void rememberBridged(TestInfo testInfo) throws IOException { // Post a reply directly to the list var conversations = mailmanList.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); - addReply(conversations.get(0), listAddress, mailmanList, pr); + addReply(conversations.get(0), listAddress, mailmanServer, pr); listServer.processIncoming(); // Another archive reader pass - has to be done twice @@ -196,7 +196,7 @@ void rememberBridged(TestInfo testInfo) throws IOException { var updated = pr.comments(); assertEquals(2, updated.size()); - var newReaderBot = new MailingListArchiveReaderBot(from, Set.of(mailmanList), Set.of(archive)); + var newReaderBot = new MailingListArchiveReaderBot(from, mailmanList, Set.of(archive)); TestBotRunner.runPeriodicItems(newReaderBot); TestBotRunner.runPeriodicItems(newReaderBot); @@ -238,8 +238,8 @@ void largeEmail(TestInfo testInfo) throws IOException { // The mailing list as well var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); - var readerBot = new MailingListArchiveReaderBot(from, Set.of(mailmanList), Set.of(archive)); + var mailmanList = mailmanServer.getListReader(listAddress.address()); + var readerBot = new MailingListArchiveReaderBot(from, mailmanList, Set.of(archive)); // Populate the projects repository var localRepo = CheckableRepository.init(tempFolder.path(), author.repositoryType()); @@ -267,7 +267,7 @@ void largeEmail(TestInfo testInfo) throws IOException { assertEquals(1, conversations.size()); var replyBody = "This line is about 30 bytes long\n".repeat(1000 * 10); - addReply(conversations.get(0), listAddress, mailmanList, pr, replyBody); + addReply(conversations.get(0), listAddress, mailmanServer, pr, replyBody); listServer.processIncoming(); // Another archive reader pass - has to be done twice diff --git a/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBotTests.java b/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBotTests.java index aff4df95d..14ea556d0 100644 --- a/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBotTests.java +++ b/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBotTests.java @@ -214,7 +214,7 @@ void simpleArchive(TestInfo testInfo) throws IOException { // The mailing list as well listServer.processIncoming(); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var conversations = mailmanList.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var mail = conversations.get(0).first(); @@ -796,7 +796,7 @@ void reviewComment(TestInfo testInfo) throws IOException { // The mailing list as well var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var conversations = mailmanList.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var mail = conversations.get(0).first(); @@ -884,7 +884,7 @@ void combineComments(TestInfo testInfo) throws IOException { // As well as the mailing list var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var conversations = mailmanList.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var mail = conversations.get(0).first(); @@ -1001,7 +1001,7 @@ void commentThreading(TestInfo testInfo) throws IOException { // Check the mailing list var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var conversations = mailmanList.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var mail = conversations.get(0).first(); @@ -1607,7 +1607,7 @@ void incrementalChanges(TestInfo testInfo) throws IOException { // Check that sender address is set properly var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var conversations = mailmanList.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); for (var newMail : conversations.get(0).allMessages()) { @@ -1734,7 +1734,7 @@ void rebased(TestInfo testInfo) throws IOException { // Check that sender address is set properly var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var conversations = mailmanList.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); for (var newMail : conversations.get(0).allMessages()) { @@ -2899,7 +2899,7 @@ void multipleRecipients(TestInfo testInfo) throws IOException { // The mail should have been sent to list1 var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress1.address()); + var mailmanList = mailmanServer.getListReader(listAddress1.address()); var conversations = mailmanList.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var mail = conversations.get(0).first(); @@ -3031,7 +3031,7 @@ void jsonArchive(TestInfo testInfo) throws IOException { // The mailing list as well listServer.processIncoming(); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var conversations = mailmanList.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var mail = conversations.get(0).first(); diff --git a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifier.java b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifier.java index a520d8227..7dacd1761 100644 --- a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifier.java +++ b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifier.java @@ -25,7 +25,7 @@ import org.openjdk.skara.bots.notify.*; import org.openjdk.skara.email.*; import org.openjdk.skara.forge.*; -import org.openjdk.skara.mailinglist.MailingList; +import org.openjdk.skara.mailinglist.*; import org.openjdk.skara.vcs.*; import org.openjdk.skara.vcs.openjdk.OpenJDKTag; @@ -37,7 +37,7 @@ import java.util.stream.Collectors; class MailingListNotifier implements Notifier, RepositoryListener { - private final MailingList list; + private final MailingListServer server; private final EmailAddress recipient; private final EmailAddress sender; private final EmailAddress author; @@ -55,10 +55,10 @@ public enum Mode { PR } - MailingListNotifier(MailingList list, EmailAddress recipient, EmailAddress sender, EmailAddress author, - boolean includeBranch, boolean reportNewTags, boolean reportNewBranches, boolean reportNewBuilds, - Mode mode, Map headers, Pattern allowedAuthorDomains) { - this.list = list; + MailingListNotifier(MailingListServer server, EmailAddress recipient, EmailAddress sender, EmailAddress author, + boolean includeBranch, boolean reportNewTags, boolean reportNewBranches, boolean reportNewBuilds, + Mode mode, Map headers, Pattern allowedAuthorDomains) { + this.server = server; this.recipient = recipient; this.sender = sender; this.author = author; @@ -208,7 +208,7 @@ private void sendCombinedCommits(HostedRepository repository, List commi .build(); try { - list.post(email); + server.post(email); } catch (RuntimeException e) { throw new NonRetriableException(e); } @@ -278,7 +278,7 @@ public void onNewOpenJDKTagCommits(HostedRepository repository, Repository local } try { - list.post(email.build()); + server.post(email.build()); } catch (RuntimeException e) { throw new NonRetriableException(e); } @@ -311,7 +311,7 @@ public void onNewTagCommit(HostedRepository repository, Repository localReposito } try { - list.post(email.build()); + server.post(email.build()); } catch (RuntimeException e) { throw new NonRetriableException(e); } @@ -369,7 +369,7 @@ public void onNewBranch(HostedRepository repository, Repository localRepository, .headers(commitHeaders(repository, commits)) .build(); try { - list.post(email); + server.post(email); } catch (RuntimeException e) { throw new NonRetriableException(e); } diff --git a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierBuilder.java b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierBuilder.java index e8d30cb0e..c1e63bd5a 100644 --- a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierBuilder.java +++ b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierBuilder.java @@ -23,13 +23,13 @@ package org.openjdk.skara.bots.notify.mailinglist; import org.openjdk.skara.email.EmailAddress; -import org.openjdk.skara.mailinglist.MailingList; +import org.openjdk.skara.mailinglist.*; import java.util.Map; import java.util.regex.Pattern; class MailingListNotifierBuilder { - private MailingList list; + private MailingListServer server; private EmailAddress recipient; private EmailAddress sender; private EmailAddress author = null; @@ -43,8 +43,8 @@ class MailingListNotifierBuilder { private boolean repoInSubject = false; private Pattern branchInSubject = Pattern.compile("a^"); // Does not match anything - public MailingListNotifierBuilder list(MailingList list) { - this.list = list; + public MailingListNotifierBuilder server(MailingListServer server) { + this.server = server; return this; } @@ -99,7 +99,7 @@ public MailingListNotifierBuilder allowedAuthorDomains(Pattern allowedAuthorDoma } public MailingListNotifier build() { - return new MailingListNotifier(list, recipient, sender, author, includeBranch, reportNewTags, reportNewBranches, - reportNewBuilds, mode, headers, allowedAuthorDomains); + return new MailingListNotifier(server, recipient, sender, author, includeBranch, reportNewTags, reportNewBranches, + reportNewBuilds, mode, headers, allowedAuthorDomains); } } diff --git a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierFactory.java b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierFactory.java index 8fd8aebd1..e0088f9f2 100644 --- a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierFactory.java +++ b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierFactory.java @@ -54,7 +54,7 @@ public Notifier create(BotConfiguration botConfiguration, JSONObject notifierCon var allowedDomains = author == null ? Pattern.compile(notifierConfiguration.get("domains").asString()) : null; var builder = MailingListNotifier.newBuilder() - .list(listServer.getList(recipient)) + .server(listServer) .recipient(recipientAddress) .sender(sender) .author(author) diff --git a/bots/notify/src/test/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierTests.java b/bots/notify/src/test/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierTests.java index 46652b999..1f940b7f4 100644 --- a/bots/notify/src/test/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierTests.java +++ b/bots/notify/src/test/java/org/openjdk/skara/bots/notify/mailinglist/MailingListNotifierTests.java @@ -52,7 +52,7 @@ void testMailingList(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -68,7 +68,7 @@ void testMailingList(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewTags(false) @@ -124,7 +124,7 @@ void testMailingListMultiple(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -140,7 +140,7 @@ void testMailingListMultiple(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewTags(false) @@ -196,7 +196,7 @@ void testMailingListMerge(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -212,7 +212,7 @@ void testMailingListMerge(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewTags(false) @@ -274,7 +274,7 @@ void testMailingListSponsored(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -290,7 +290,7 @@ void testMailingListSponsored(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewTags(false) @@ -338,7 +338,7 @@ void testMailingListMultipleBranches(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -355,7 +355,7 @@ void testMailingListMultipleBranches(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .author(author) @@ -436,7 +436,7 @@ void testMailingListPROnlyMultipleBranches(TestInfo testInfo) throws IOException var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -453,7 +453,7 @@ void testMailingListPROnlyMultipleBranches(TestInfo testInfo) throws IOException .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .author(author) @@ -486,7 +486,7 @@ void testMailingListPROnlyMultipleBranches(TestInfo testInfo) throws IOException var rfr = Email.create(sender, "RFR: My PR", "PR: " + pr.webUrl().toString()) .recipient(listAddress) .build(); - mailmanList.post(rfr); + mailmanServer.post(rfr); listServer.processIncoming(); // And an integration (but it hasn't reached master just yet) @@ -522,7 +522,7 @@ void testMailingListPR(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -538,7 +538,7 @@ void testMailingListPR(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewTags(false) @@ -570,7 +570,7 @@ void testMailingListPR(TestInfo testInfo) throws IOException { .author(EmailAddress.from("duke", "duke@duke.duke")) .recipient(listAddress) .build(); - mailmanList.post(rfr); + mailmanServer.post(rfr); listServer.processIncoming(); // And an integration @@ -613,7 +613,7 @@ void testMailingListMergePR(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -629,7 +629,7 @@ void testMailingListMergePR(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewTags(false) @@ -669,7 +669,7 @@ void testMailingListMergePR(TestInfo testInfo) throws IOException { .author(EmailAddress.from("duke", "duke@duke.duke")) .recipient(listAddress) .build(); - mailmanList.post(rfr); + mailmanServer.post(rfr); listServer.processIncoming(); // And an integration @@ -710,7 +710,7 @@ void testMailingListPROnce(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -726,7 +726,7 @@ void testMailingListPROnce(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .author(null) @@ -755,7 +755,7 @@ void testMailingListPROnce(TestInfo testInfo) throws IOException { .author(EmailAddress.from("duke", "duke@duke.duke")) .recipient(listAddress) .build(); - mailmanList.post(rfr); + mailmanServer.post(rfr); listServer.processIncoming(); // And an integration @@ -806,7 +806,7 @@ void testMailinglistTag(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -822,7 +822,7 @@ void testMailinglistTag(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewBranches(false) @@ -831,7 +831,7 @@ void testMailinglistTag(TestInfo testInfo) throws IOException { updater.attachTo(notifyBot); var noTagsUpdater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewTags(false) @@ -925,7 +925,7 @@ void testMailinglistPlainTags(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -941,7 +941,7 @@ void testMailinglistPlainTags(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewBranches(false) @@ -950,7 +950,7 @@ void testMailinglistPlainTags(TestInfo testInfo) throws IOException { .build(); updater.attachTo(notifyBot); var noTagsUpdater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewTags(false) @@ -1019,7 +1019,7 @@ void testMailingListBranch(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -1035,7 +1035,7 @@ void testMailingListBranch(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewTags(false) @@ -1099,7 +1099,7 @@ void testMailingListNoIdempotence(TestInfo testInfo) throws IOException { var listAddress = EmailAddress.parse(listServer.createList("test")); var mailmanServer = MailingListServerFactory.createMailmanServer(listServer.getArchive(), listServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress.address()); + var mailmanList = mailmanServer.getListReader(listAddress.address()); var tagStorage = createTagStorage(repo); var branchStorage = createBranchStorage(repo); var prStateStorage = createPullRequestStateStorage(repo); @@ -1115,7 +1115,7 @@ void testMailingListNoIdempotence(TestInfo testInfo) throws IOException { .prStateStorageBuilder(prStateStorage) .build(); var updater = MailingListNotifier.newBuilder() - .list(mailmanList) + .server(mailmanServer) .recipient(listAddress) .sender(sender) .reportNewTags(false) diff --git a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/Conversation.java b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/Conversation.java index ff3bb304c..303d359c7 100644 --- a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/Conversation.java +++ b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/Conversation.java @@ -22,25 +22,36 @@ */ package org.openjdk.skara.mailinglist; -import org.openjdk.skara.email.Email; +import org.openjdk.skara.email.*; import java.util.*; +import java.util.function.Function; +import java.util.stream.*; public class Conversation { private final Email first; - private final Map> replies = new LinkedHashMap<>(); - private final Map parents = new HashMap<>(); + private final Map> replies = new LinkedHashMap<>(); + private final Map parents = new HashMap<>(); Conversation(Email first) { this.first = first; - replies.put(first, new ArrayList<>()); + replies.put(first.id(), new LinkedHashSet<>()); } void addReply(Email parent, Email reply) { - var replyList = replies.get(parent); - replyList.add(reply); - replies.put(reply, new ArrayList<>()); - parents.put(reply, parent); + if (!replies.containsKey(reply.id())) { + var replyList = replies.get(parent.id()); + replyList.add(reply); + replies.put(reply.id(), new LinkedHashSet<>()); + } + if (!parents.containsKey(reply.id())) { + parents.put(reply.id(), parent); + } else { + var oldParent = parents.get(reply.id()); + if (!parent.equals(oldParent)) { + throw new RuntimeException("Email with id " + reply.id() + " seen with multiple parents: " + oldParent.id() + " and " + parent.id()); + } + } } public Email first() { @@ -48,25 +59,20 @@ public Email first() { } public List replies(Email parent) { - return new ArrayList<>(replies.get(parent)); + return new ArrayList<>(replies.get(parent.id())); } public List allMessages() { - return new ArrayList<>(replies.keySet()); + var unordered = Stream.concat(Stream.of(List.of(first)), replies.values().stream()) + .flatMap(Collection::stream) + .collect(Collectors.toMap(Email::id, Function.identity())); + return replies.keySet().stream() + .map(unordered::get) + .collect(Collectors.toList()); } public Email parent(Email email) { - return parents.get(email); - } - - public List allParents(Email email) { - var emailParents = new ArrayList(); - while (parents.containsKey(email)) { - var parent = parents.get(email); - emailParents.add(parent); - email = parent; - } - return emailParents; + return parents.get(email.id()); } @Override diff --git a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingList.java b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListReader.java similarity index 91% rename from mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingList.java rename to mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListReader.java index 620f14362..529062123 100644 --- a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingList.java +++ b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListReader.java @@ -22,12 +22,9 @@ */ package org.openjdk.skara.mailinglist; -import org.openjdk.skara.email.*; - import java.time.Duration; -import java.util.*; +import java.util.List; -public interface MailingList { - void post(Email email); +public interface MailingListReader { List conversations(Duration maxAge); } diff --git a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListServer.java b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListServer.java index 3fd81ab59..4289da19f 100644 --- a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListServer.java +++ b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListServer.java @@ -22,6 +22,9 @@ */ package org.openjdk.skara.mailinglist; +import org.openjdk.skara.email.Email; + public interface MailingListServer { - MailingList getList(String name); + MailingListReader getListReader(String... listNames); + void post(Email email); } diff --git a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListServerFactory.java b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListServerFactory.java index 9a42137b9..9435ce42b 100644 --- a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListServerFactory.java +++ b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/MailingListServerFactory.java @@ -30,7 +30,6 @@ import java.time.Duration; public class MailingListServerFactory { - public static MailingListServer createMailmanServer(URI archive, String smtp, Duration sendInterval) { return new MailmanServer(archive, smtp, sendInterval); } diff --git a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/Mbox.java b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/Mbox.java index b9b30c3de..cb09ddd82 100644 --- a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/Mbox.java +++ b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/Mbox.java @@ -42,7 +42,7 @@ public class Mbox { private final static Pattern fromStringEncodePattern = Pattern.compile("^(>*From )", Pattern.MULTILINE); private final static Pattern fromStringDecodePattern = Pattern.compile("^>(>*From )", Pattern.MULTILINE); - private static List splitMbox(String mbox, EmailAddress sender) { + public static List splitMbox(String mbox, EmailAddress sender) { // Initial split var messages = mboxMessagePattern.matcher(mbox).results() .map(match -> match.group(1)) @@ -81,14 +81,9 @@ private static String decodeFromStrings(String body) { return fromStringMatcher.replaceAll("$1"); } - public static List parseMbox(String mbox) { - return parseMbox(mbox, null); - } - private static final Pattern inReplyToPattern = Pattern.compile("<(.*@.*?)>"); - public static List parseMbox(String mbox, EmailAddress sender) { - var emails = splitMbox(mbox, sender); + public static List parseMbox(List emails) { var idToMail = emails.stream().collect(Collectors.toMap(Email::id, Function.identity(), (a, b) -> a)); var idToConversation = idToMail.values().stream() .filter(email -> !email.hasHeader("In-Reply-To")) @@ -115,7 +110,6 @@ public static List parseMbox(String mbox, EmailAddress sender) { var parent = idToMail.get(inReplyTo); if (!idToConversation.containsKey(inReplyTo)) { outOfOrder.add(email); - log.info("Can't find conversation: " + inReplyTo + " - possibly out of order"); } else { var conversation = idToConversation.get(inReplyTo); conversation.addReply(parent, email); @@ -126,6 +120,7 @@ public static List parseMbox(String mbox, EmailAddress sender) { } if (!outOfOrder.isEmpty()) { log.info("Out of order remaining: " + outOfOrder.size()); + outOfOrder.forEach(oo -> log.info(" " + oo.id())); } return idToConversation.values().stream() diff --git a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mailman/MailmanList.java b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mailman/MailmanListReader.java similarity index 70% rename from mailinglist/src/main/java/org/openjdk/skara/mailinglist/mailman/MailmanList.java rename to mailinglist/src/main/java/org/openjdk/skara/mailinglist/mailman/MailmanListReader.java index de67b1981..d2aaed756 100644 --- a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mailman/MailmanList.java +++ b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mailman/MailmanListReader.java @@ -34,9 +34,9 @@ import java.util.logging.Logger; import java.util.stream.Collectors; -public class MailmanList implements MailingList { +public class MailmanListReader implements MailingListReader { private final MailmanServer server; - private final EmailAddress listAddress; + private final List names = new ArrayList<>(); private final Logger log = Logger.getLogger("org.openjdk.skara.mailinglist"); private final ConcurrentMap> pageCache = new ConcurrentHashMap<>(); private List cachedConversations = new ArrayList<>(); @@ -44,19 +44,20 @@ public class MailmanList implements MailingList { .connectTimeout(Duration.ofSeconds(10)) .build(); - MailmanList(MailmanServer server, EmailAddress name) { + MailmanListReader(MailmanServer server, Collection names) { this.server = server; - this.listAddress = name; + for (var name : names) { + if (name.contains("@")) { + this.names.add(EmailAddress.parse(name).localPart()); + } else { + this.names.add(name); + } + } } @Override public String toString() { - return "MailmanList:" + listAddress; - } - - @Override - public void post(Email email) { - server.sendMessage(email); + return "MailmanList:" + String.join(", ", names); } private List getMonthRange(Duration maxAge) { @@ -75,7 +76,7 @@ private List getMonthRange(Duration maxAge) { return ret; } - private Optional> getPage(HttpClient client, URI uri) { + private Optional> getPage(URI uri) { var requestBuilder = HttpRequest.newBuilder(uri) .timeout(Duration.ofSeconds(30)) .GET(); @@ -114,39 +115,44 @@ public List conversations(Duration maxAge) { .sorted(Comparator.reverseOrder()) .collect(Collectors.toList()); - var actualPages = new LinkedList(); - var useCached = false; + var useCached = new HashMap(); + for (var name : names) { + useCached.put(name, false); + } var newContent = false; + var emails = new ArrayList(); for (var month : potentialPages) { - URI mboxUri = server.getMbox(listAddress.localPart(), month); - - if (useCached) { - var cachedResponse = pageCache.get(mboxUri); - if (cachedResponse == null) { - break; - } else { - actualPages.addFirst(cachedResponse.body()); - } - } else { - var mboxResponse = getPage(client, mboxUri); - if (mboxResponse.isEmpty()) { - break; - } - if (mboxResponse.get().statusCode() == 304) { - actualPages.addFirst(pageCache.get(mboxUri).body()); - useCached = true; + for (var name : names) { + URI mboxUri = server.getMbox(name, month); + var sender = EmailAddress.from(name + "@" + mboxUri.getHost()); + + if (useCached.get(name)) { + var cachedResponse = pageCache.get(mboxUri); + if (cachedResponse == null) { + break; + } else { + emails.addAll(0, Mbox.splitMbox(cachedResponse.body(), sender)); + } } else { - actualPages.addFirst(mboxResponse.get().body()); - newContent = true; + var mboxResponse = getPage(mboxUri); + if (mboxResponse.isEmpty()) { + break; + } + if (mboxResponse.get().statusCode() == 304) { + emails.addAll(0, Mbox.splitMbox(pageCache.get(mboxUri).body(), sender)); + useCached.put(name, true); + } else { + emails.addAll(0, Mbox.splitMbox(mboxResponse.get().body(), sender)); + newContent = true; + } } } } if (newContent) { - var concatenatedMbox = String.join("", actualPages); - var mails = Mbox.parseMbox(concatenatedMbox, listAddress); + var conversations = Mbox.parseMbox(emails); var threshold = ZonedDateTime.now().minus(maxAge); - cachedConversations = mails.stream() + cachedConversations = conversations.stream() .filter(mail -> mail.first().date().isAfter(threshold)) .collect(Collectors.toList()); } diff --git a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mailman/MailmanServer.java b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mailman/MailmanServer.java index b0c21aeb8..748b6118e 100644 --- a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mailman/MailmanServer.java +++ b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mailman/MailmanServer.java @@ -23,20 +23,20 @@ package org.openjdk.skara.mailinglist.mailman; import org.openjdk.skara.email.*; -import org.openjdk.skara.network.URIBuilder; import org.openjdk.skara.mailinglist.*; +import org.openjdk.skara.network.URIBuilder; import java.io.*; import java.net.URI; import java.time.*; import java.time.format.DateTimeFormatter; -import java.util.Locale; +import java.util.*; public class MailmanServer implements MailingListServer { private final URI archive; private final String smtpServer; + private final Duration sendInterval; private volatile Instant lastSend; - private Duration sendInterval; public MailmanServer(URI archive, String smtpServer, Duration sendInterval) { this.archive = archive; @@ -46,7 +46,7 @@ public MailmanServer(URI archive, String smtpServer, Duration sendInterval) { } URI getMbox(String listName, ZonedDateTime month) { - var dateStr = DateTimeFormatter.ofPattern("YYYY-MMMM", Locale.US).format(month); + var dateStr = DateTimeFormatter.ofPattern("yyyy-MMMM", Locale.US).format(month); return URIBuilder.base(archive).appendPath(listName + "/" + dateStr + ".txt").build(); } @@ -66,7 +66,12 @@ void sendMessage(Email message) { } @Override - public MailingList getList(String name) { - return new MailmanList(this, EmailAddress.parse(name)); + public void post(Email email) { + sendMessage(email); + } + + @Override + public MailingListReader getListReader(String... listNames) { + return new MailmanListReader(this, Arrays.asList(listNames)); } } diff --git a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mboxfile/MboxFileList.java b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mboxfile/MboxFileList.java deleted file mode 100644 index 02f9d6956..000000000 --- a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mboxfile/MboxFileList.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package org.openjdk.skara.mailinglist.mboxfile; - -import org.openjdk.skara.email.*; -import org.openjdk.skara.mailinglist.*; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.*; -import java.time.*; -import java.util.*; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -public class MboxFileList implements MailingList { - private final Path file; - private final EmailAddress recipient; - private final Logger log = Logger.getLogger("org.openjdk.skara.mailinglist"); - - MboxFileList(Path file, EmailAddress recipient) { - this.file = file.resolveSibling(file.getFileName() + ".mbox"); - this.recipient = recipient; - } - - private void postNewConversation(Email mail) { - var mboxMail = Mbox.fromMail(mail); - if (Files.notExists(file)) { - if (Files.notExists(file.getParent())) { - try { - Files.createDirectories(file.getParent()); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - } - try { - Files.writeString(file, mboxMail, StandardCharsets.UTF_8, StandardOpenOption.APPEND); - } catch (IOException e) { - try { - Files.writeString(file, mboxMail, StandardCharsets.UTF_8, StandardOpenOption.CREATE_NEW); - } catch (IOException e1) { - throw new UncheckedIOException(e); - } - } - } - - private void postReply(Email mail) { - var mboxMail = Mbox.fromMail(mail); - try { - Files.writeString(file, mboxMail, StandardCharsets.UTF_8, StandardOpenOption.APPEND); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - @Override - public void post(Email email) { - if (email.hasHeader(("In-Reply-To"))) { - postReply(email); - } else { - postNewConversation(email); - } - } - - @Override - public List conversations(Duration maxAge) { - String mbox; - try { - mbox = Files.readString(file, StandardCharsets.UTF_8); - } catch (IOException e) { - log.info("Failed to open mbox file"); - return new LinkedList<>(); - } - var cutoff = Instant.now().minus(maxAge); - return Mbox.parseMbox(mbox).stream() - .filter(email -> email.first().date().toInstant().isAfter(cutoff)) - .collect(Collectors.toList()); - } -} diff --git a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mboxfile/MboxFileListReader.java b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mboxfile/MboxFileListReader.java new file mode 100644 index 000000000..70506f678 --- /dev/null +++ b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mboxfile/MboxFileListReader.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.skara.mailinglist.mboxfile; + +import org.openjdk.skara.email.*; +import org.openjdk.skara.mailinglist.*; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; +import java.time.*; +import java.util.*; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class MboxFileListReader implements MailingListReader { + private final Path base; + private final Collection names; + private final Logger log = Logger.getLogger("org.openjdk.skara.mailinglist"); + + MboxFileListReader(Path base, Collection names) { + this.base = base; + this.names = names; + } + + @Override + public List conversations(Duration maxAge) { + var emails = new ArrayList(); + for (var name : names) { + try { + var file = base.resolve(name + ".mbox"); + var currentMbox = Files.readString(file, StandardCharsets.UTF_8); + emails.addAll(Mbox.splitMbox(currentMbox, EmailAddress.from(name + "@mbox.file"))); + } catch (IOException e) { + log.info("Failed to open mbox file"); + } + } + if (emails.isEmpty()) { + return new LinkedList<>(); + } + var cutoff = Instant.now().minus(maxAge); + return Mbox.parseMbox(emails).stream() + .filter(email -> email.first().date().toInstant().isAfter(cutoff)) + .collect(Collectors.toList()); + } +} diff --git a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mboxfile/MboxFileListServer.java b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mboxfile/MboxFileListServer.java index 79a513553..793d6813d 100644 --- a/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mboxfile/MboxFileListServer.java +++ b/mailinglist/src/main/java/org/openjdk/skara/mailinglist/mboxfile/MboxFileListServer.java @@ -22,12 +22,14 @@ */ package org.openjdk.skara.mailinglist.mboxfile; -import org.openjdk.skara.email.EmailAddress; +import org.openjdk.skara.email.Email; import org.openjdk.skara.mailinglist.*; -import java.net.URLEncoder; +import java.io.*; import java.nio.charset.StandardCharsets; -import java.nio.file.Path; +import java.nio.file.*; +import java.util.Arrays; +import java.util.stream.Collectors; public class MboxFileListServer implements MailingListServer { Path base; @@ -36,9 +38,52 @@ public MboxFileListServer(Path base) { this.base = base; } + private void postNewConversation(Path file, Email mail) { + var mboxMail = Mbox.fromMail(mail); + if (Files.notExists(file)) { + if (Files.notExists(file.getParent())) { + try { + Files.createDirectories(file.getParent()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + try { + Files.writeString(file, mboxMail, StandardCharsets.UTF_8, StandardOpenOption.APPEND); + } catch (IOException e) { + try { + Files.writeString(file, mboxMail, StandardCharsets.UTF_8, StandardOpenOption.CREATE_NEW); + } catch (IOException e1) { + throw new UncheckedIOException(e); + } + } + } + + private void postReply(Path file, Email mail) { + var mboxMail = Mbox.fromMail(mail); + try { + Files.writeString(file, mboxMail, StandardCharsets.UTF_8, StandardOpenOption.APPEND); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public void post(Email email) { + var recipientList = email.recipients().stream() + .map(e -> base.resolve(e.localPart() + ".mbox")) + .collect(Collectors.toList()); + + if (email.hasHeader(("In-Reply-To"))) { + recipientList.forEach(list -> postReply(list, email)); + } else { + recipientList.forEach(list -> postNewConversation(list, email)); + } + } + @Override - public MailingList getList(String name) { - var recipient = URLEncoder.encode(name, StandardCharsets.US_ASCII) + "@localhost"; - return new MboxFileList(base.resolve(name), EmailAddress.from("", recipient)); + public MailingListReader getListReader(String... listNames) { + return new MboxFileListReader(base, Arrays.asList(listNames)); } } diff --git a/mailinglist/src/test/java/org/openjdk/skara/mailinglist/MailmanTests.java b/mailinglist/src/test/java/org/openjdk/skara/mailinglist/MailmanTests.java index f802673f9..0b4c53341 100644 --- a/mailinglist/src/test/java/org/openjdk/skara/mailinglist/MailmanTests.java +++ b/mailinglist/src/test/java/org/openjdk/skara/mailinglist/MailmanTests.java @@ -22,11 +22,10 @@ */ package org.openjdk.skara.mailinglist; +import org.junit.jupiter.api.Test; import org.openjdk.skara.email.*; import org.openjdk.skara.test.TestMailmanServer; -import org.junit.jupiter.api.Test; - import java.io.IOException; import java.time.Duration; @@ -39,12 +38,12 @@ void simple() throws IOException { var listAddress = testServer.createList("test"); var mailmanServer = MailingListServerFactory.createMailmanServer(testServer.getArchive(), testServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress); + var mailmanList = mailmanServer.getListReader(listAddress); var sender = EmailAddress.from("Test", "test@test.email"); var mail = Email.create(sender, "Subject", "Body") .recipient(EmailAddress.parse(listAddress)) .build(); - mailmanList.post(mail); + mailmanServer.post(mail); var expectedMail = Email.from(mail) .sender(EmailAddress.parse(listAddress)) .build(); @@ -64,12 +63,12 @@ void replies() throws IOException { var listAddress = testServer.createList("test"); var mailmanServer = MailingListServerFactory.createMailmanServer(testServer.getArchive(), testServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress); + var mailmanList = mailmanServer.getListReader(listAddress); var sender = EmailAddress.from("Test", "test@test.email"); var sentParent = Email.create(sender, "Subject", "Body") .recipient(EmailAddress.parse(listAddress)) .build(); - mailmanList.post(sentParent); + mailmanServer.post(sentParent); testServer.processIncoming(); var expectedParent = Email.from(sentParent) .sender(EmailAddress.parse(listAddress)) @@ -85,7 +84,7 @@ void replies() throws IOException { .recipient(EmailAddress.parse(listAddress)) .header("In-Reply-To", sentParent.id().toString()) .build(); - mailmanList.post(sentReply); + mailmanServer.post(sentReply); var expectedReply = Email.from(sentReply) .sender(EmailAddress.parse(listAddress)) .build(); @@ -110,12 +109,12 @@ void cached() throws IOException { var listAddress = testServer.createList("test"); var mailmanServer = MailingListServerFactory.createMailmanServer(testServer.getArchive(), testServer.getSMTP(), Duration.ZERO); - var mailmanList = mailmanServer.getList(listAddress); + var mailmanList = mailmanServer.getListReader(listAddress); var sender = EmailAddress.from("Test", "test@test.email"); var mail = Email.create(sender, "Subject", "Body") .recipient(EmailAddress.parse(listAddress)) .build(); - mailmanList.post(mail); + mailmanServer.post(mail); testServer.processIncoming(); var expectedMail = Email.from(mail) @@ -144,7 +143,7 @@ void interval() throws IOException { var listAddress = testServer.createList("test"); var mailmanServer = MailingListServerFactory.createMailmanServer(testServer.getArchive(), testServer.getSMTP(), Duration.ofDays(1)); - var mailmanList = mailmanServer.getList(listAddress); + var mailmanList = mailmanServer.getListReader(listAddress); var sender = EmailAddress.from("Test", "test@test.email"); var mail1 = Email.create(sender, "Subject 1", "Body 1") .recipient(EmailAddress.parse(listAddress)) @@ -153,8 +152,8 @@ void interval() throws IOException { .recipient(EmailAddress.parse(listAddress)) .build(); new Thread(() -> { - mailmanList.post(mail1); - mailmanList.post(mail2); + mailmanServer.post(mail1); + mailmanServer.post(mail2); }).start(); var expectedMail = Email.from(mail1) .sender(EmailAddress.parse(listAddress)) diff --git a/mailinglist/src/test/java/org/openjdk/skara/mailinglist/MboxTests.java b/mailinglist/src/test/java/org/openjdk/skara/mailinglist/MboxTests.java index 02d881e2f..2e167405a 100644 --- a/mailinglist/src/test/java/org/openjdk/skara/mailinglist/MboxTests.java +++ b/mailinglist/src/test/java/org/openjdk/skara/mailinglist/MboxTests.java @@ -39,15 +39,20 @@ class MboxTests { void simple() { try (var folder = new TemporaryDirectory()) { var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); - var list = mbox.getList("test"); + var list = mbox.getListReader("test"); var sender = EmailAddress.from("test", "test@test.mail"); - var sentMail = Email.create(sender, "Subject", "Message").build(); - list.post(sentMail); + var sentMail = Email.create(sender, "Subject", "Message") + .recipient(EmailAddress.from("test@mbox")) + .build(); + var expectedMail = Email.from(sentMail) + .sender(EmailAddress.from("test@mbox.file")) + .build(); + mbox.post(sentMail); var conversations = list.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var conversation = conversations.get(0); - assertEquals(sentMail, conversation.first()); + assertEquals(expectedMail, conversation.first()); } } @@ -55,29 +60,38 @@ void simple() { void multiple() { try (var folder = new TemporaryDirectory()) { var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); - var list = mbox.getList("test"); + var list = mbox.getListReader("test"); var sender1 = EmailAddress.from("test1", "test1@test.mail"); var sender2 = EmailAddress.from("test2", "test2@test.mail"); - var sentParent = Email.create(sender1, "Subject 1", "Message 1").build(); - list.post(sentParent); + var sentParent = Email.create(sender1, "Subject 1", "Message 1") + .recipient(EmailAddress.from("test@mbox")) + .build(); + var expectedParent = Email.from(sentParent) + .sender(EmailAddress.from("test@mbox.file")) + .build(); + mbox.post(sentParent); var conversations = list.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var sentReply = Email.create(sender2, "Subject 2", "Message 2") + .recipient(EmailAddress.from("test@mbox")) .header("In-Reply-To", sentParent.id().toString()) .header("References", sentParent.id().toString()) .build(); - list.post(sentReply); + var expectedReply = Email.from(sentReply) + .sender(EmailAddress.from("test@mbox.file")) + .build(); + mbox.post(sentReply); conversations = list.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var conversation = conversations.get(0); - assertEquals(sentParent, conversation.first()); - var replies = conversation.replies(sentParent); + assertEquals(expectedParent, conversation.first()); + var replies = conversation.replies(expectedParent); assertEquals(1, replies.size()); var reply = replies.get(0); - assertEquals(sentReply, reply); + assertEquals(expectedReply, reply); } } @@ -85,42 +99,32 @@ void multiple() { void uninitialized() { try (var folder = new TemporaryDirectory()) { var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); - var list = mbox.getList("test"); + var list = mbox.getListReader("test"); var conversations = list.conversations(Duration.ofDays(1)); assertEquals(0, conversations.size()); } } - @Test - void nested() { - try (var folder = new TemporaryDirectory()) { - var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); - var list = mbox.getList("this/is/a/nested/path/test"); - - var sender = EmailAddress.from("test", "test@test.mail"); - var sentMail = Email.create(sender, "Subject", "Message").build(); - list.post(sentMail); - var conversations = list.conversations(Duration.ofDays(1)); - assertEquals(1, conversations.size()); - var conversation = conversations.get(0); - assertEquals(sentMail, conversation.first()); - } - } - @Test void differentAuthor() { try (var folder = new TemporaryDirectory()) { var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); - var list = mbox.getList("test"); + var list = mbox.getListReader("test"); var sender = EmailAddress.from("test1", "test1@test.mail"); var author = EmailAddress.from("test2", "test2@test.mail"); - var sentMail = Email.create(author, "Subject", "Message").sender(sender).build(); - list.post(sentMail); + var sentMail = Email.create(author, "Subject", "Message") + .recipient(EmailAddress.from("test@mbox")) + .sender(sender) + .build(); + var expectedMail = Email.from(sentMail) + .sender(EmailAddress.from("test@mbox.file")) + .build(); + mbox.post(sentMail); var conversations = list.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var conversation = conversations.get(0); - assertEquals(sentMail, conversation.first()); + assertEquals(expectedMail, conversation.first()); } } @@ -128,19 +132,24 @@ void differentAuthor() { void encodedFrom() { try (var folder = new TemporaryDirectory()) { var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); - var list = mbox.getList("test"); + var list = mbox.getListReader("test"); var sender = EmailAddress.from("test", "test@test.mail"); var sentMail = Email.create(sender, "Subject", """ - From is an odd way to start - From may also be the second row - >>From as a quote - And From in the middle""").build(); - list.post(sentMail); + From is an odd way to start + From may also be the second row + >>From as a quote + And From in the middle""") + .recipient(EmailAddress.from("test@mbox")) + .build(); + var expectedMail = Email.from(sentMail) + .sender(EmailAddress.from("test@mbox.file")) + .build(); + mbox.post(sentMail); var conversations = list.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var conversation = conversations.get(0); - assertEquals(sentMail, conversation.first()); + assertEquals(expectedMail, conversation.first()); } } @@ -148,15 +157,20 @@ void encodedFrom() { void utf8Encode() { try (var folder = new TemporaryDirectory()) { var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); - var list = mbox.getList("test"); + var list = mbox.getListReader("test"); var sender = EmailAddress.from("têßt", "test@test.mail"); - var sentMail = Email.create(sender, "Sübjeçt", "(╯°□°)╯︵ ┻━┻").build(); - list.post(sentMail); + var sentMail = Email.create(sender, "Sübjeçt", "(╯°□°)╯︵ ┻━┻") + .recipient(EmailAddress.from("test@mbox")) + .build(); + var expectedMail = Email.from(sentMail) + .sender(EmailAddress.from("test@mbox.file")) + .build(); + mbox.post(sentMail); var conversations = list.conversations(Duration.ofDays(1)); assertEquals(1, conversations.size()); var conversation = conversations.get(0); - assertEquals(sentMail, conversation.first()); + assertEquals(expectedMail, conversation.first()); } } @@ -176,7 +190,7 @@ void unencodedFrom() throws IOException { From this point onwards, it may be hard to parse this """, StandardCharsets.UTF_8); var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); - var list = mbox.getList("test"); + var list = mbox.getListReader("test"); var conversations = list.conversations(Duration.ofDays(365 * 100)); assertEquals(1, conversations.size()); var conversation = conversations.get(0); @@ -210,7 +224,7 @@ void replyToWithExtra() throws IOException { """, StandardCharsets.UTF_8); var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); - var list = mbox.getList("test"); + var list = mbox.getListReader("test"); var conversations = list.conversations(Duration.ofDays(365 * 100)); assertEquals(1, conversations.size()); var conversation = conversations.get(0); @@ -251,7 +265,69 @@ void replyOutOfOrder() throws IOException { """, StandardCharsets.UTF_8); var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); - var list = mbox.getList("test"); + var list = mbox.getListReader("test"); + var conversations = list.conversations(Duration.ofDays(365 * 100)); + assertEquals(1, conversations.size()); + var conversation = conversations.get(0); + assertEquals(3, conversation.allMessages().size()); + } + } + + @Test + void replyCross() throws IOException { + try (var folder = new TemporaryDirectory()) { + var rawMbox1 = folder.path().resolve("test1.mbox"); + Files.writeString(rawMbox1, """ + From test at example.com Wed Aug 21 17:22:50 2019 + From: test at example.com (test at example.com) + Date: Wed, 21 Aug 2019 17:22:50 +0000 + Subject: this is a test + Message-ID: + + First message + + From test2 at example.com Wed Aug 21 17:32:50 2019 + From: test2 at example.com (test2 at example.com) + Date: Wed, 21 Aug 2019 17:32:50 +0000 + Subject: Re: this is a test + In-Reply-To: (This be a reply) + Message-ID: + + Second message + + From test3 at example.com Wed Aug 21 17:42:50 2019 + From: test3 at example.com (test3 at example.com) + Date: Wed, 21 Aug 2019 17:42:50 +0000 + Subject: Re: this is a test + In-Reply-To: + Message-ID: + + Third message + """, + StandardCharsets.UTF_8); + var rawMbox2 = folder.path().resolve("test2.mbox"); + Files.writeString(rawMbox2, """ + From test3 at example.com Wed Aug 21 17:42:50 2019 + From: test3 at example.com (test3 at example.com) + Date: Wed, 21 Aug 2019 17:42:50 +0000 + Subject: Re: this is a test + In-Reply-To: + Message-ID: + + Third message + + From test2 at example.com Wed Aug 21 17:32:50 2019 + From: test2 at example.com (test2 at example.com) + Date: Wed, 21 Aug 2019 17:32:50 +0000 + Subject: Re: this is a test + In-Reply-To: (This be a reply) + Message-ID: + + Second message + """, + StandardCharsets.UTF_8); + var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); + var list = mbox.getListReader("test1", "test2"); var conversations = list.conversations(Duration.ofDays(365 * 100)); assertEquals(1, conversations.size()); var conversation = conversations.get(0); @@ -259,4 +335,47 @@ void replyOutOfOrder() throws IOException { } } + @Test + void replyOutOfOrderSplit() throws IOException { + try (var folder = new TemporaryDirectory()) { + var rawMbox1 = folder.path().resolve("test1.mbox"); + Files.writeString(rawMbox1, """ + From test at example.com Wed Aug 21 17:22:50 2019 + From: test at example.com (test at example.com) + Date: Wed, 21 Aug 2019 17:22:50 +0000 + Subject: this is a test + Message-ID: + + First message + + From test3 at example.com Wed Aug 21 17:42:50 2019 + From: test3 at example.com (test3 at example.com) + Date: Wed, 21 Aug 2019 17:42:50 +0000 + Subject: Re: this is a test + In-Reply-To: + Message-ID: + + Third message + """, + StandardCharsets.UTF_8); + var rawMbox2 = folder.path().resolve("test2.mbox"); + Files.writeString(rawMbox2, """ + From test2 at example.com Wed Aug 21 17:32:50 2019 + From: test2 at example.com (test2 at example.com) + Date: Wed, 21 Aug 2019 17:32:50 +0000 + Subject: Re: this is a test + In-Reply-To: (This be a reply) + Message-ID: + + Second message + """, + StandardCharsets.UTF_8); + var mbox = MailingListServerFactory.createMboxFileServer(folder.path()); + var list = mbox.getListReader("test1", "test2"); + var conversations = list.conversations(Duration.ofDays(365 * 100)); + assertEquals(1, conversations.size()); + var conversation = conversations.get(0); + assertEquals(3, conversation.allMessages().size()); + } + } } diff --git a/test/src/main/java/org/openjdk/skara/test/TestMailmanServer.java b/test/src/main/java/org/openjdk/skara/test/TestMailmanServer.java index a553f3b13..a8a9abee4 100644 --- a/test/src/main/java/org/openjdk/skara/test/TestMailmanServer.java +++ b/test/src/main/java/org/openjdk/skara/test/TestMailmanServer.java @@ -101,7 +101,7 @@ public String getSMTP() { } public String createList(String name) throws IOException { - var listName = EmailAddress.parse(name + "@testserver.test").toString(); + var listName = EmailAddress.parse(name + "@" + httpServer.getAddress().getHostString()).toString(); var listPath = Files.createTempFile("list-" + name, ".txt"); lists.put(name, listPath); return listName;