I'm using GNU NNTP to connect to leafnode, which is an NNTP server, on localhost. The GNU API utilizes javax.mail.Message
, which comes with the following caveat:
From the Message API:
..the message number for a particular Message can change during a session if other messages in the Folder are deleted and expunged.
So, currently, I'm using javax.mail.search
to search for a known message. Unfortunately, for each search the entire folder has be searched. I could keep the folder open and in that way speed the search a bit, but it just seems klunky.
What's an alternate approach to using javax.mail.search
? This:
SearchTerm st = new MessageIDTerm(id);
List<Message> messages = Arrays.asList(folder.search(st));
works fine when the javax.mail.Folder
only has a few Message
's. However, for very large Folder
's there must be a better approach. Instead of the Message-ID
header field, Xref
might be preferable, but still has the same fundamental problem of searching strings.
Here's the database, which just needs to hold enough information to find/get/search the Folder
's for a specified message:
mysql>
mysql> use usenet;show tables;
Database changed
+------------------+
| Tables_in_usenet |
+------------------+
| articles |
| newsgroups |
+------------------+
2 rows in set (0.00 sec)
mysql>
mysql> describe articles;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| ID | bigint(20) | NO | PRI | NULL | auto_increment |
| MESSAGEID | varchar(255) | YES | | NULL | |
| NEWSGROUP_ID | bigint(20) | YES | MUL | NULL | |
+--------------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql>
mysql> describe newsgroups;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| ID | bigint(20) | NO | PRI | NULL | auto_increment |
| NEWSGROUP | varchar(255) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql>
While the schema is very simple at the moment, I plan to add complexity to it.
messages are queried for with getMessage()
:
package net.bounceme.dur.usenet.model;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.*;
import javax.mail.search.MessageIDTerm;
import javax.mail.search.SearchTerm;
import net.bounceme.dur.usenet.controller.Page;
public enum Usenet {
INSTANCE;
private final Logger LOG = Logger.getLogger(Usenet.class.getName());
private Properties props = new Properties();
private Folder root = null;
private Store store = null;
private List<Folder> folders = new ArrayList<>();
private Folder folder = null;
Usenet() {
LOG.fine("controller..");
props = PropertiesReader.getProps();
try {
connect();
} catch (Exception ex) {
Logger.getLogger(Usenet.class.getName()).log(Level.SEVERE, "FAILED TO LOAD MESSAGES", ex);
}
}
public void connect() throws Exception {
LOG.fine("Usenet.connect..");
Session session = Session.getDefaultInstance(props);
session.setDebug(true);
store = session.getStore(new URLName(props.getProperty("nntp.host")));
store.connect();
root = store.getDefaultFolder();
setFolders(Arrays.asList(root.listSubscribed()));
}
public List<Message> getMessages(Page page) throws Exception {
Newsgroup newsgroup = new Newsgroup(page);
LOG.fine("fetching.." + newsgroup);
folder = root.getFolder(newsgroup.getNewsgroup());
folder.open(Folder.READ_ONLY);
List<Message> messages = Arrays.asList(folder.getMessages());
LOG.fine("..fetched " + folder);
return Collections.unmodifiableList(messages);
}
public List<Folder> getFolders() {
LOG.fine("folders " + folders);
return Collections.unmodifiableList(folders);
}
private void setFolders(List<Folder> folders) {
this.folders = folders;
}
public Message getMessage(Newsgroup newsgroup, Article article) throws MessagingException {
LOG.fine("\n\ntrying.." + newsgroup + article);
String id = article.getMessageId();
Message message = null;
folder = root.getFolder(newsgroup.getNewsgroup());
folder.open(Folder.READ_ONLY);
SearchTerm st = new MessageIDTerm(id);
List<Message> messages = Arrays.asList(folder.search(st));
LOG.severe(messages.toString());
if (!messages.isEmpty()) {
message = messages.get(0);
}
LOG.info(message.getSubject());
return message;
}
}
The problem, which I'm only now realizing, is that:
...the message number for a particular Message can change during a session if other messages in the Folder are deleted and expunged.
Regardless of which particular header is used, it's something like:
Message-ID: <[email protected]>
or
Xref: dur.bounceme.net gwene.com.economist:541
So that there's always a String which needs parsing and searching, which is quite awkward.
I do notice that MimeMessage
has a very convenient getMessageID method. Unfortunately, GNU uses javax.mail.Message
and not MimeMessage
. Granted, it's possible to instantiate a folder and MimeMessage
, but I don't see any savings there in that from one run to another there's no guarantee that getMessageID
will return the correct message.
The awkward solution I see is to maybe create a persistent folder of MimeMessage
's, but that seems like overkill.
Hence, using a header, either Xref
or Message-ID
and then parsing and searching strings...
Is there a better way?
javax.mail
is a lowest-common-denominator API, and it's behavior depends entirely on what is the backend. So, without knowing what you are talking to, it's not really possible to give a good answer to your question. Chances are, however, that you'll need to talk directly to whatever you're talking to and learn more about its behavior.This might be a comment rather than an answer, but I'm thinking that the information that this API is just a thin layer might be enough information to justify.