Proviso: I don't want to use a database for the following, please.
I'm getting a closed socket exception, which I attribute to latency from leafnode running on localhost. It's expected that there will be latency, I'm just not sure how to deal with it.
I know, from a separate CLI program, that leafnode's replying correctly and that the utility class is working. For this app, I can see from glassfish logs:
nntp: <68 <[email protected]>
nntp: <69 <[email protected]>
nntp: <70 <[email protected]>
nntp: <71 <[email protected]>
nntp: <72 <[email protected]>
nntp: <73 <[email protected]>
nntp: <74 <[email protected]>
nntp: <.
nntp: >QUIT
nntp: <205 Always happy to serve!
INFO: GOT MESSAGES AS SO..
INFO: [gnu.mail.providers.nntp.NNTPMessage@1e2218b, gnu.mail.providers.nntp.NNTPMessage@10838ec, gnu.mail.providers.nntp.NNTPMessage@d14f1b, gnu.mail.providers.nntp.NNTPMessage@1a0b187, gnu.mail.providers.nntp.NNTPMessage@e74409, gnu.mail.providers.nntp.NNTPMessage@123ce36, gnu.mail.providers.nntp.NNTPMessage@1b7515a, gnu.mail.providers.nntp.NNTPMessage@3e0a57, gnu.mail.providers.nntp.NNTPMessage@118d5e0, gnu.mail.providers.nntp.NNTPMessage@1819b07, gnu.mail.providers.nntp.NNTPMessage@1e91886, gnu.mail.providers.nntp.NNTPMessage@493ab5, gnu.mail.providers.nntp.NNTPMessage@1ad7ec5, gnu.mail.providers.nntp.NNTPMessage@15dbf8d, gnu.mail.providers.nntp.NNTPMessage@1cae695, gnu.mail.providers.nntp.NNTPMessage@1e86f47, gnu.mail.providers.nntp.NNTPMessage@e1d295, gnu.mail.providers.nntp.NNTPMessage@1c87547, gnu.mail.providers.nntp.NNTPMessage@18b9691, gnu.mail.providers.nntp.NNTPMessage@14a0c6f, gnu.mail.providers.nntp.NNTPMessage@765340, gnu.mail.providers.nntp.NNTPMessage@53b2e4, gnu.mail.providers.nntp.NNTPMessage@10597ed, gnu.mail.providers.nntp.NNTPMessage@396c01, gnu.mail.providers.nntp.NNTPMessage@1e1f03d, gnu.mail.providers.nntp.NNTPMessage@63410a, gnu.mail.providers.nntp.NNTPMessage@433231, gnu.mail.providers.nntp.NNTPMessage@1945928, gnu.mail.providers.nntp.NNTPMessage@696ea0, gnu.mail.providers.nntp.NNTPMessage@1dde855, gnu.mail.providers.nntp.NNTPMessage@1c0559e, gnu.mail.providers.nntp.NNTPMessage@352a35, gnu.mail.providers.nntp.NNTPMessage@660b04, gnu.mail.providers.nntp.NNTPMessage@15d401f, gnu.mail.providers.nntp.NNTPMessage@11cddfb, gnu.mail.providers.nntp.NNTPMessage@31917d, gnu.mail.providers.nntp.NNTPMessage@46be43, gnu.mail.providers.nntp.NNTPMessage@7523ed, gnu.mail.providers.nntp.NNTPMessage@89a405, gnu.mail.providers.nntp.NNTPMessage@6f5b1b, gnu.mail.providers.nntp.NNTPMessage@c649d6, gnu.mail.providers.nntp.NNTPMessage@19882d, gnu.mail.providers.nntp.NNTPMessage@3b8201, gnu.mail.providers.nntp.NNTPMessage@d44937, gnu.mail.providers.nntp.NNTPMessage@112e45c, gnu.mail.providers.nntp.NNTPMessage@14e8cfc, gnu.mail.providers.nntp.NNTPMessage@a15e3, gnu.mail.providers.nntp.NNTPMessage@22f041, gnu.mail.providers.nntp.NNTPMessage@1cb71d7, gnu.mail.providers.nntp.NNTPMessage@19ee882, gnu.mail.providers.nntp.NNTPMessage@1b2d490, gnu.mail.providers.nntp.NNTPMessage@1a16b05, gnu.mail.providers.nntp.NNTPMessage@79412e, gnu.mail.providers.nntp.NNTPMessage@a66063, gnu.mail.providers.nntp.NNTPMessage@10203ea, gnu.mail.providers.nntp.NNTPMessage@14f0db5, gnu.mail.providers.nntp.NNTPMessage@10ceef3, gnu.mail.providers.nntp.NNTPMessage@1bc33e, gnu.mail.providers.nntp.NNTPMessage@af5b1a, gnu.mail.providers.nntp.NNTPMessage@860956, gnu.mail.providers.nntp.NNTPMessage@1cf1146, gnu.mail.providers.nntp.NNTPMessage@1770fb1, gnu.mail.providers.nntp.NNTPMessage@1a76bc3, gnu.mail.providers.nntp.NNTPMessage@940c94, gnu.mail.providers.nntp.NNTPMessage@1c5d10c, gnu.mail.providers.nntp.NNTPMessage@1fa00d, gnu.mail.providers.nntp.NNTPMessage@44ecf0, gnu.mail.providers.nntp.NNTPMessage@11f88e8, gnu.mail.providers.nntp.NNTPMessage@1554648, gnu.mail.providers.nntp.NNTPMessage@17921a7, gnu.mail.providers.nntp.NNTPMessage@1905dbf, gnu.mail.providers.nntp.NNTPMessage@fad175]
INFO: ..GOT MESSAGES AS SO
that the NNTP messages are, ultimately, getting loaded. It's just that it doesn't seem to get back in time to the bean, or, if it does get back, it might be the wrong instance of the bean. I'm inferring that the first bean gets through fine, but another bean gets a socket problem because Leafnode is busy. It looks like the bean constructor is being invoked more than once, which is of course ok, but might be why the socket is closed for the second bean.
So, how do I isolate the latency problem so that any MessageBean gets the, err, ?enum singleton? of javax.mail.Message's correctly? (I'm using the GNU NNTP library, which is so much better then Apache, and works fine.)
Here is the facelets client:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
template="./template.xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:f="http://java.sun.com/jsf/core">
<ui:define name="left">
<h:button id="submit" value="click me" />
</ui:define>
<ui:define name="content">
<ui:repeat value="#{messageBean.messages}" var="message">
<li>
<h:outputText value="#{message.messageNumber}" />
<h:outputText value="#{message.subject}" />
</li>
</ui:repeat>
</ui:define>
</ui:composition>
and the backing MessageBean as so:
package net.bounceme.dur.nntp;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import javax.mail.Message;
@Named
@SessionScoped
public class MessageBean implements Serializable {
private static final long serialVersionUID = 1;
private static Logger logger = Logger.getLogger(MessageBean.class.getName());
private static Level level = Level.INFO;
public MessageBean() {
logger.log(level, "MessageBean..");
}
public List<Message> getMessages() throws Exception {
logger.log(level, "MessageBean.getMessages..");
List<Message> messages = new ArrayList<Message>();
messages = NNTP.getMessages();
logger.log(level, "GOT MESSAGES AS SO..");
logger.log(level, messages.toString());
logger.log(level, "..GOT MESSAGES AS SO");
return messages;
}
}
Now, MessageBean invokes the utility NNTP class, which interacts with leafnode on localhost, so it's quite fast from the CLI. However, not fast enough, apparently, for facelets, because I keep getting socket errors.
From logging to Glassfish, I see that leafnode's returning the articles to the utility class correctly, and that, eventually, MessageBean even has a large List.
The problem appears to be that there are, perhaps, multiple instances of MessageBean, as there should be. However, they're not all going to be able query leafnode simultaneously, presumably, and be successful. Perhaps this is the source of the socket error.
In any event, I do not want to persist a javax.mail.Message to a database, leafnode already persists the messages fine. I strictly want to interface with leafnode on localhost.
Do I need some sort dreaded enum singleton? It's the only "pattern" I know, so I figure it's gotta apply in this scenario. Or, is there some alternate way to slow down MessageBean to be sure that it actually gets its messages from the utility NNTP class before passing them on to the facelet? Or, would such a delay cause an error of some sort?
What's a good approach here? I'm not trying to build a full-featured NNTP client, just fiddling about with leafnode, which has some latency.
solution: close sockets properly and use a container class as in setEntities below :
package net.bounceme.dur.nntp;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.*;
public enum SingletonNNTP {
INSTANCE;
private final Logger logger = Logger.getLogger(SingletonNNTP.class.getName());
private final Level level = Level.INFO;
private Properties props = new Properties();
private List<Message> messages = new ArrayList<Message>();
private boolean loaded = false;
private List<MessageEntity> messageEntities = new ArrayList<MessageEntity>();
private SingletonNNTP() {
logger.logp(level, "SingletonNNTP", "SingletonNNTP", "only once...");
props = PropertiesReader.getProps();
if (!loaded) {
try {
loaded = setMessages();
} catch (Exception ex) {
Logger.getLogger(SingletonNNTP.class.getName()).log(Level.SEVERE, "FAILED TO LOAD MESSAGES", ex);
}
}
}
public List<Message> getMessages(boolean debug) throws Exception {
logger.logp(level, "SingletonNNTP", "getMessages", "returning messages");
return Collections.unmodifiableList(messages);
}
private boolean setMessages() throws Exception {
logger.logp(level, "SingletonNNTP", "setMessages", "connecting to leafnode");
Session session = Session.getDefaultInstance(props);
session.setDebug(false);
Store store = session.getStore(new URLName(props.getProperty("nntp.host")));
store.connect();
Folder root = store.getDefaultFolder();
Folder folder = root.getFolder(props.getProperty("nntp.group"));
folder.open(Folder.READ_ONLY);
Message[] msgs = folder.getMessages();
messages = Arrays.asList(msgs);
setEntities();
folder.close(false);
store.close();
return true;
}
public List<MessageEntity> getEntities() {
logger.logp(level, "SingletonNNTP", "getEntities", "getting entities");
for (MessageEntity m : messageEntities) {
for (Header h : m.getHeaders()) {
logger.log(level, h.toString());
}
}
return Collections.unmodifiableList(messageEntities);
}
private void setEntities() throws Exception {
logger.logp(level, "SingletonNNTP", "loadEntities", "trying to convert");
messageEntities = new ArrayList<MessageEntity>();
for (Message message : messages) {
MessageEntity entity = new MessageEntity();
Enumeration allHeaders = message.getAllHeaders();
List<Header> headers = new ArrayList<Header>();
while (allHeaders.hasMoreElements()) {
Header hdr = (Header) allHeaders.nextElement();
headers.add(hdr);
}
entity.setHeaders(headers);
entity.setSubject(message.getSubject());
entity.setContent(message.getContent().toString());
entity.setSentDate(message.getReceivedDate());
messageEntities.add(entity);
}
}
}
From what I understand from your problem, you actually need to have a synchronized concurrent access to NNTP.getMessages()
. If so and you're using managed beans in an EJB context, I believe Managing Concurrent Access in a Singleton Session Bean can help you. If not directly helpful, I believe you can take advantage of ReentrantLock
to test if concurrent control over NNTP
can resolve the issue.