/*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.util.*;
import java.util.concurrent.atomic.*;
import java.io.*;
import javax.mail.*;
import javax.mail.event.*;
import javax.mail.internet.*;
import javax.activation.*;
import com.sun.mail.imap.*;
/**
* Test program for IDLE support in IMAP provider.
*
* Run several threads that access the folder's connection
* while another thread runs the IDLE command. A timer
* thread checks that each thread is making progress, to
* detect deadlock.
*
* XXX - Should have another thread add messages to the folder
* to test the new message notification works properly.
*/
public class testidle {
public static Store store;
public static Folder folder;
public static boolean showStructure = false;
public static boolean saveAttachments = false;
public static int attnum = 0;
public static int totalTime;
public static final AtomicBoolean stop = new AtomicBoolean(false);
public static final AtomicInteger folderProgress = new AtomicInteger();
public static final AtomicInteger msgProgress = new AtomicInteger();
public static void main(String argv[]) {
if (argv.length != 5) {
System.out.println(
"Usage: testidle <host> <user> <password> <mbox> <time>");
System.exit(1);
}
try {
Properties props = System.getProperties();
// Get a Session object
Session session = Session.getInstance(props, null);
// session.setDebug(true);
// Get a Store object
store = session.getStore("imap");
store.addStoreListener(new StoreListener() {
public void notification(StoreEvent e) {
System.out.println("StoreEvent: type " +
e.getMessageType() + ", message " +
e.getMessage());
}
});
// Connect
store.connect(argv[0], argv[1], argv[2]);
// Open a Folder
folder = store.getFolder(argv[3]);
if (folder == null || !folder.exists()) {
System.out.println("Invalid folder");
System.exit(1);
}
folder.open(Folder.READ_WRITE);
// Add messageCountListener to listen for new messages
folder.addMessageCountListener(new MessageCountAdapter() {
public void messagesAdded(MessageCountEvent ev) {
Message[] msgs = ev.getMessages();
System.out.println("Got " + msgs.length + " new messages");
// Just dump out the new messages
for (int i = 0; i < msgs.length; i++) {
try {
System.out.println("-----");
System.out.println("Message " +
msgs[i].getMessageNumber() + ":");
msgs[i].writeTo(System.out);
} catch (IOException ioex) {
ioex.printStackTrace();
} catch (MessagingException mex) {
mex.printStackTrace();
}
}
}
});
totalTime = Integer.parseInt(argv[4]);
new Thread("timer") {
public void run() {
timer();
}
}.start();
new Thread("message reader") {
public void run() {
readMessages();
}
}.start();
new Thread("folder reader") {
public void run() {
readFolder();
}
}.start();
new Thread("store idle") {
public void run() {
storeIdle();
}
}.start();
/*
* make sure two threads running idle works properly
new Thread("idle") {
public void run() {
try {
doIdle();
} catch (MessagingException mex) { }
}
}.start();
*/
doIdle();
System.out.println("connected " + store.isConnected());
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Run the idle command until told to stop.
*/
public static void doIdle() throws MessagingException {
boolean supportsIdle = false;
try {
if (folder instanceof IMAPFolder) {
IMAPFolder f = (IMAPFolder)folder;
f.idle();
supportsIdle = true;
}
} catch (FolderClosedException fex) {
throw fex;
} catch (MessagingException mex) {
supportsIdle = false;
}
while (!stop.get()) {
if (supportsIdle && folder instanceof IMAPFolder) {
IMAPFolder f = (IMAPFolder)folder;
f.idle();
/*
System.out.println("IDLE done in " +
Thread.currentThread().getName());
*/
} else {
try {
Thread.sleep(1000); // sleep for 1000 milliseconds
} catch (InterruptedException ex) { }
// This is to force the IMAP server to send us
// EXISTS notifications.
folder.getMessageCount();
}
}
}
/**
* Monitor progress of threads and tell them to stop
* when their time is up.
*/
public static void timer() {
long tend = System.currentTimeMillis() + totalTime;
int fpcnt = folderProgress.get();
int mpcnt = msgProgress.get();
while (System.currentTimeMillis() < tend) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) { }
int nfpcnt = folderProgress.get();
int nmpcnt = msgProgress.get();
if (!(nfpcnt > fpcnt && nmpcnt > mpcnt)) {
System.out.println("THREAD STUCK?");
System.out.printf("fpcnt %d nfpcnt %d\n", fpcnt, nfpcnt);
System.out.printf("mpcnt %d nmpcnt %d\n", mpcnt, nmpcnt);
}
fpcnt = nfpcnt;
mpcnt = nmpcnt;
}
System.out.println("STOPPING");
stop.set(true);
try {
folder.getUnreadMessageCount(); // force IDLE to terminate
} catch (MessagingException mex) { }
}
/**
* Read all the messages in the folder and access all their content.
*/
public static void readMessages() {
try {
while (!stop.get()) {
int cnt = folder.getMessageCount();
for (int i = 1; i <= cnt; i++) {
//System.out.println("dump " + i);
Message msg = folder.getMessage(i);
dumpEnvelope(msg);
msgProgress.incrementAndGet();
try {
Thread.sleep(25);
} catch (InterruptedException ex) { }
}
}
System.out.println("messages DONE");
} catch (Exception ex) {
System.out.println(ex);
}
}
/**
* Perform folder commands to compete with the message
* access commands.
*/
public static void readFolder() {
try {
while (!stop.get()) {
int cnt = folder.getUnreadMessageCount();
store.isConnected(); // poke the store too
folderProgress.incrementAndGet();
try {
Thread.sleep(100);
} catch (InterruptedException ex) { }
}
System.out.println("folder DONE");
} catch (MessagingException mex) {
System.out.println(mex);
}
}
/**
* Run the idle command until told to stop.
*/
public static void storeIdle() {
boolean supportsIdle = false;
try {
if (store instanceof IMAPStore) {
IMAPStore s = (IMAPStore)store;
s.idle();
supportsIdle = true;
}
} catch (MessagingException mex) {
supportsIdle = false;
}
try {
while (!stop.get()) {
if (supportsIdle && store instanceof IMAPStore) {
IMAPStore s = (IMAPStore)store;
s.idle();
/*
*/
System.out.println("IDLE done in " +
Thread.currentThread().getName());
/*
*/
} else {
try {
Thread.sleep(1000); // sleep for 1000 milliseconds
} catch (InterruptedException ex) { }
}
}
} catch (MessagingException mex) {
System.out.println(mex);
}
}
/**
* Dump contents of message part.
* (Copied from msgshow.java)
*/
public static void dumpPart(Part p) throws Exception {
if (p instanceof Message)
dumpEnvelope((Message)p);
/** Dump input stream ..
InputStream is = p.getInputStream();
// If "is" is not already buffered, wrap a BufferedInputStream
// around it.
if (!(is instanceof BufferedInputStream))
is = new BufferedInputStream(is);
int c;
while ((c = is.read()) != -1)
System.out.write(c);
**/
String ct = p.getContentType();
try {
pr("CONTENT-TYPE: " + (new ContentType(ct)).toString());
} catch (ParseException pex) {
pr("BAD CONTENT-TYPE: " + ct);
}
String filename = p.getFileName();
if (filename != null)
pr("FILENAME: " + filename);
/*
* Using isMimeType to determine the content type avoids
* fetching the actual content data until we need it.
*/
if (p.isMimeType("text/plain")) {
pr("This is plain text");
pr("---------------------------");
if (!showStructure && !saveAttachments)
System.out.println((String)p.getContent());
} else if (p.isMimeType("multipart/*")) {
pr("This is a Multipart");
pr("---------------------------");
Multipart mp = (Multipart)p.getContent();
level++;
int count = mp.getCount();
for (int i = 0; i < count; i++)
dumpPart(mp.getBodyPart(i));
level--;
} else if (p.isMimeType("message/rfc822")) {
pr("This is a Nested Message");
pr("---------------------------");
level++;
dumpPart((Part)p.getContent());
level--;
} else {
if (!showStructure && !saveAttachments) {
/*
* If we actually want to see the data, and it's not a
* MIME type we know, fetch it and check its Java type.
*/
Object o = p.getContent();
if (o instanceof String) {
pr("This is a string");
pr("---------------------------");
System.out.println((String)o);
} else if (o instanceof InputStream) {
pr("This is just an input stream");
pr("---------------------------");
InputStream is = (InputStream)o;
int c;
while ((c = is.read()) != -1)
System.out.write(c);
} else {
pr("This is an unknown type");
pr("---------------------------");
pr(o.toString());
}
} else {
// just a separator
pr("---------------------------");
}
}
/*
* If we're saving attachments, write out anything that
* looks like an attachment into an appropriately named
* file. Don't overwrite existing files to prevent
* mistakes.
*/
if (saveAttachments && level != 0 && !p.isMimeType("multipart/*")) {
String disp = p.getDisposition();
// many mailers don't include a Content-Disposition
if (disp == null || disp.equalsIgnoreCase(Part.ATTACHMENT)) {
if (filename == null)
filename = "Attachment" + attnum++;
pr("Saving attachment to file " + filename);
try {
File f = new File(filename);
if (f.exists())
// XXX - could try a series of names
throw new IOException("file exists");
((MimeBodyPart)p).saveFile(f);
} catch (IOException ex) {
pr("Failed to save attachment: " + ex);
}
pr("---------------------------");
}
}
}
public static void dumpEnvelope(Message m) throws Exception {
pr("This is the message envelope");
pr("---------------------------");
Address[] a;
// FROM
if ((a = m.getFrom()) != null) {
for (int j = 0; j < a.length; j++)
pr("FROM: " + a[j].toString());
}
// TO
if ((a = m.getRecipients(Message.RecipientType.TO)) != null) {
for (int j = 0; j < a.length; j++) {
pr("TO: " + a[j].toString());
InternetAddress ia = (InternetAddress)a[j];
if (ia.isGroup()) {
InternetAddress[] aa = ia.getGroup(false);
for (int k = 0; k < aa.length; k++)
pr(" GROUP: " + aa[k].toString());
}
}
}
// SUBJECT
pr("SUBJECT: " + m.getSubject());
// DATE
Date d = m.getSentDate();
pr("SendDate: " +
(d != null ? d.toString() : "UNKNOWN"));
// FLAGS
Flags flags = m.getFlags();
StringBuffer sb = new StringBuffer();
Flags.Flag[] sf = flags.getSystemFlags(); // get the system flags
boolean first = true;
for (int i = 0; i < sf.length; i++) {
String s;
Flags.Flag f = sf[i];
if (f == Flags.Flag.ANSWERED)
s = "\\Answered";
else if (f == Flags.Flag.DELETED)
s = "\\Deleted";
else if (f == Flags.Flag.DRAFT)
s = "\\Draft";
else if (f == Flags.Flag.FLAGGED)
s = "\\Flagged";
else if (f == Flags.Flag.RECENT)
s = "\\Recent";
else if (f == Flags.Flag.SEEN)
s = "\\Seen";
else
continue; // skip it
if (first)
first = false;
else
sb.append(' ');
sb.append(s);
}
String[] uf = flags.getUserFlags(); // get the user flag strings
for (int i = 0; i < uf.length; i++) {
if (first)
first = false;
else
sb.append(' ');
sb.append(uf[i]);
}
pr("FLAGS: " + sb.toString());
// X-MAILER
String[] hdrs = m.getHeader("X-Mailer");
if (hdrs != null)
pr("X-Mailer: " + hdrs[0]);
else
pr("X-Mailer NOT available");
}
static String indentStr = " ";
static int level = 0;
/**
* Print a, possibly indented, string.
*/
public static void pr(String s) {
/*
if (showStructure)
System.out.print(indentStr.substring(0, level * 2));
System.out.println(s);
*/
}
}