Reputation: 413
I'm currently developing an email application with a background service used in conjunction with the JavaMail Idle functionality. The background service keeps the idle functionality working by issuing a check every 10 minutes (as the server being used (Exchange server)) can sometimes drop the connection anywhere between 10 and 30 minutes of being connected.
Whilst this is currently working great and the idle/push functionality works great, occasionally the below exception is thrown:
java.util.concurrent.TimeoutException: com.sun.mail.imap.IMAPSSLStore.finalize() timed out after 10 seconds
This version of JavaMail is being used where no other issues have been encountered through using it:
//JavaMail
compile 'com.sun.mail:android-mail:1.5.5'
Checking our bug reporting system, this has caused the application to crash on 30 different occasions. The code for the idle and keep alive functionality can be seen below:
public void checkInboxEmail(final String host, final String user, final String password) {
Log.d(TAG, "checkEmail");
this.host = host;
this.user = user;
this.password = password;
new Thread(new Runnable() {
@Override
public void run() {
try {
Log.d(TAG, "checkEmail - run()");
long databaseRecords;
//create properties field
Properties properties = new Properties();
properties.put("mail.store.protocol", "imaps");
properties.put("mail.imaps.ssl.trust", "*");
//properties.put("mail.debug", "true");
emailSession = Session.getInstance(properties, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password);
}
});
imapStore = (IMAPStore) emailSession.getStore("imaps");
// imapStore.connect();
imapStore.connect(host, user, password);
if (imapStore.isConnected()) {
Log.d("MailPush", "Successfully connected to IMAP");
} else {
Log.d("MailPush", "Not connected to IMAP");
}
imapFolder = (IMAPFolder) imapStore.getFolder("Inbox");
imapFolder.open(IMAPFolder.READ_WRITE);
databaseRecords = dbManager.getReceivedEmailRecordsCount();
if (databaseRecords < imapFolder.getMessageCount()) {
Log.d(TAG, "Receiving Mail...");
receiveMail(imapFolder.getMessages());
} else {
Log.d(TAG, "Records match.");
}
Folder[] fdr = imapStore.getDefaultFolder().list();
for (Folder fd : fdr)
System.out.println(">> " + fd.getName());
imapFolder.addMessageCountListener(new MessageCountListener() {
public void messagesAdded(MessageCountEvent e) {
System.out.println("Message Added Event Fired");
Log.d(TAG, "MESSAGE TYPE: " + e.getType());
//ADDED = 1 & REMOVED = 2
Boolean proceed = true;
try {
Message[] messages = e.getMessages();
Log.d(TAG, "Last Subject 1: " + lastSubject + " " + "Last Date: " + lastDate);
Log.d(TAG, "Current Subject 1: " + messages[0].getSubject() + " " + "Last Date: " + messages[0].getSentDate().toString());
if (lastSubject != null && lastDate != null) {
Log.d(TAG, "Existing last subject or date");
if (lastSubject.equals(messages[0].getSubject()) && lastDate.equals(messages[0].getSentDate().toString())) {
Log.d(TAG, "Stopping proceed");
proceed = false;
} else {
Log.d(TAG, "Allowing proceed");
proceed = true;
lastSubject = messages[0].getSubject();
lastDate = messages[0].getSentDate().toString();
}
} else {
Log.d(TAG, "No existing last subject or date");
lastSubject = messages[0].getSubject();
lastDate = messages[0].getSentDate().toString();
}
Log.d(TAG, "Last Subject 2: " + lastSubject + " " + "Last Date: " + lastDate);
Log.d(TAG, "Current Subject 2: " + messages[0].getSubject() + " " + "Last Date: " + messages[0].getSentDate().toString());
if (proceed) {
Log.d(TAG, "Proceeding...");
System.out.println("messages.length---" + messages.length);
getMessages(messages);
} else {
Log.d(TAG, "Not Proceeding...");
}
} catch (Exception ex) {
Log.d(TAG, "CATCH 2");
ex.printStackTrace();
}
}
public void messagesRemoved(MessageCountEvent e) {
System.out.println("Message Removed Event fired");
}
});
imapFolder.addMessageChangedListener(new MessageChangedListener() {
public void messageChanged(MessageChangedEvent e) {
System.out.println("Message Changed Event fired");
}
});
startListening(imapFolder);
//close the store and folder objects
// emailFolder.close(false);
// store.close();
} catch (MessagingException e) {
Log.d(TAG, "CATCH 3");
AndroidUtils.closeEmailFolder(imapFolder);
AndroidUtils.closeEmailStore(imapStore);
Handler handler = new Handler(Looper.getMainLooper());
Runnable runnable = new Runnable() {
@Override
public void run() {
checkConnectivity();
}
};
handler.postDelayed(runnable, 30 * 1000); // 30 seconds
} catch (Exception e) {
Log.d(TAG, "CATCH 4");
e.printStackTrace();
}
}
}).start();
}
private void getMessages(Message[] messages) {
Log.d(TAG, "getMessages()");
try {
for (Message message : messages) {
if (!message.getFlags().contains(Flags.Flag.SEEN)) {
message.setFlag(Flags.Flag.SEEN, true);
if (message.getSubject().startsWith("<confirm>")) {
Log.d(TAG, "Sender is looking for a confirm receipt");
sendConfirmReceipt(message);
}
//Message is new (hasn't been seen) > Message Details
System.out.println("---------------------------------");
System.out.println("Email Number " + (message.getMessageNumber()));
System.out.println("Subject: " + message.getSubject());
System.out.println("From: " + message.getFrom()[0]);
System.out.println("Text: " + message.getContent().toString());
String from = message.getFrom()[0].toString();
String cc = InternetAddress.toString(message.getRecipients(Message.RecipientType.CC));
Log.d(TAG, "CC 1: " + cc);
Address[] recipients = message.getRecipients(Message.RecipientType.CC);
cc = InternetAddress.toString(recipients);
Log.d(TAG, "CC 2: " + cc);
//Check Encryption Details > Add SEEN Flag > Add to database
checkEncryption((MimeMessage) message, from, cc);
}
}
} catch (Exception ex) {
Log.d(TAG, "CATCH 5");
ex.printStackTrace();
}
}
private void startListening(IMAPFolder imapFolder) {
Log.d(TAG, "startListening");
// We need to create a new thread to keep alive the connection
Thread t = new Thread(
new KeepAliveRunnable(imapFolder), "IdleConnectionKeepAlive"
);
t.start();
while (!Thread.interrupted()) {
Log.d(TAG, "Starting IDLE");
try {
Log.d(TAG, "Setting IDLE");
//getMessages(imapFolder.getMessages());
imapFolder.idle();
} catch (FolderClosedException fex) {
Log.d(TAG, "CATCH 6");
//Server closes connection.
Log.d(TAG, "FolderClosedException. Server potentially dropped connection. Retrying connection...");
fex.printStackTrace();
AndroidUtils.closeEmailFolder(imapFolder);
AndroidUtils.closeEmailStore(imapStore);
checkConnectivity();
} catch (MessagingException e) {
Log.d(TAG, "CATCH 7");
//Idle isn't supported by server.
Log.d(TAG, "Messaging exception during IDLE: ");
e.printStackTrace();
AndroidUtils.closeEmailFolder(imapFolder);
}
}
// Shutdown keep alive thread
if (t.isAlive()) {
Log.d(TAG, "Interrupting thread");
t.interrupt();
}
}
private static class KeepAliveRunnable implements Runnable {
private final String TAG = getClass().getName();
private static final long KEEP_ALIVE_FREQ = 60000 * 10; // 10 minutes (Exchange connection drops after 20-30 minutes)
private IMAPFolder folder;
KeepAliveRunnable(IMAPFolder folder) {
this.folder = folder;
}
@Override
public void run() {
while (!Thread.interrupted()) {
try {
Thread.sleep(KEEP_ALIVE_FREQ);
// Perform a messageCount check just to keep alive the connection
Log.d(TAG, "Performing a messageCount check to keep the connection alive");
folder.getMessageCount();
} catch (InterruptedException e) {
Log.d(TAG, "CATCH 7");
// Ignore, just aborting the thread...
Log.d(TAG, "Interrupted...");
e.printStackTrace();
} catch (MessagingException e) {
Log.d(TAG, "CATCH 8");
// Shouldn't really happen...
Log.d(TAG, "Unexpected exception while keeping alive the IDLE connection");
e.printStackTrace();
}
}
}
}
Therefore, how can I keep the idle function working and use the check/interrupt thread to constantly continue to receive emails without the 'TimeoutException' being thrown?
Upvotes: 0
Views: 491
Reputation: 29971
Make sure you're really closing all the connections when you're done with them. try/finally is your friend.
In case you miss one, there was a fix put into JavaMail 1.5.6 for this case.
Upvotes: 1