3

I've got a problem I can't really figure out. I have my main thread, and in it I want to

  1. Send an email with an attachment
  2. Delete the files that were attached

in that order. My problem is that I am using an email helper that I don't have control over, and it generates another thread that does the sending. Because of this, my files are being deleted before they are done being attached, and I am getting a FNF error in the mailer. I am looking to find a way to make my main thread wait until the files are done being attached. I don't know how long that will take. I don't have control over the other thread creation, so I can't use join(). Is there something I can use with Transport maybe, or a way to wait for all threads made in a method/class to stop?

The layout of my program is

//do stuff
MailHelper.sendMail(...); //thread is created somewhere in this method
deleteFiles(); //this happens before sendMail is finished!

I need to use Java 6. Worst-case I can have my main thread sleep for a few seconds, but that's unideal. Any help is appreciated

radar
  • 595
  • 2
  • 11
  • Does MailHelper give you any indication that sending is finished? Can you attach a listener? – sjr Jul 31 '14 at 15:58
  • No it doesn't. I can't modify the class at all either – radar Jul 31 '14 at 15:59
  • How can you tell the mail is sent? Do you have any way to check? – Jean Logeart Jul 31 '14 at 16:00
  • Can you really not modify the method so that it returns a ``Future>``? – Jean Logeart Jul 31 '14 at 16:02
  • 6
    sounds like you're out of luck, use a better mail sender – sjr Jul 31 '14 at 16:02
  • As far as I can tell there's nothing that would indicate it's done...the method that's called starts a new thread, in which it calls `Transport.send()`, then exits. No kind of status variables or anything – radar Jul 31 '14 at 16:02
  • what types of objects are you passing to `sendMail()`? – jtahlborn Jul 31 '14 at 16:05
  • It spawns a thread out of nowhere? You should use an other API! – Jean Logeart Jul 31 '14 at 16:07
  • I agree, it really isn't a great API, but I'm restricted to it. I pass `sendMail()` a message, subject, and attachment path. The class has all other info needed. Thanks for the input but it looks like I'm just going to have the thread sleep for a while...it's an automated process so hopefully it won't hurt too much. – radar Jul 31 '14 at 16:12
  • @KevinWalters I second to sjr. You need your own implementation of send mail if you want execute sequentially those things. – Paul Vargas Jul 31 '14 at 16:52
  • 1
    Hey guys, I've been thinking and might have a solution - what if I made a new `ThreadGroup`, made a new thread for calling `sendMail()`, and put that thread in the thread group. Then I'd do a join on that thread - after that thread finishes, the new thread I am trying to wait for will have been created, so I would get all threads in that `ThreadGroup`, and join on all of them - then when they're all done, I can continue! Does this sound reasonable? I've never used `ThreadGroup` before so my reasoning may be off. – radar Jul 31 '14 at 17:12
  • As an extreme measure, you might try by holding a `Reference` (`java.lang.ref`) to the path argument in your thread and delete the file after the reference has been queued... it's hard to tell whether it will work without the source of your `MailSender`... `MailSender` could use it to construct some other object (`java.io.File`?) and the string could be garbage-collected before the mail has been sent. If it works, however, it will probably be one of the dirtiest hacks in history... – giorgiga Jul 31 '14 at 17:14
  • If by "automated process" you mean the JVM exits after the emails are sent (and with enough disk space to hold all attachments in the batch), you might just use `deleteOnExit` (`java.io.File`) – giorgiga Jul 31 '14 at 17:30

2 Answers2

2

This is an interesting question! Basically you want to wait for all child threads to complete, but have no control over them.

Here is a demonstration of the technique using ThreadGroup:

Assuming you have a MailHelper class like this:

public class MailHelper {

    public void sendMail(){
        Thread t = new Thread(new Runnable() {

            @Override
            public void run() {
                System.out.println("MailHelper: Sending mail for 6s");
                for(int i = 0; i < 6; i++){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(".");
                }
                System.out.println("MailHelper: Sent mail!");
            }
        });
        t.start();
    }

}

then our Main class demonstrates how use it:

public class Main {

    public static void main(String[] args) throws InterruptedException {
        final MailHelper mh = new MailHelper();

        ThreadGroup mailThreadGroup = new ThreadGroup("mailGroup");
        Thread callSendmailThread = new Thread(mailThreadGroup, new Runnable() {

            @Override
            public void run() {
                System.out.println("Calling sendMail().");
                mh.sendMail();
                System.out.println("sendMail() returned.");
            }
        });
        callSendmailThread.start();
        callSendmailThread.join();
        System.out.println("callSendmailThread joined. Waiting for rest of ThreadGroup to finish.");

        // We cannot rely on ThreadGroup.activeCount() to give us an accurate number, and it could be zero!
        Thread[] mailThreads = new Thread[mailThreadGroup.activeCount() + 1];
        //Therefore retry enumerate until our array was large enough to hold all
        while ( mailThreadGroup.enumerate( mailThreads, true ) == mailThreads.length ) {
            mailThreads = new Thread[ mailThreads.length * 2 ];
        }

        for(Thread t : mailThreads){
            if(t != null && t.isAlive()){
                System.out.println("Joining thread " + t.getName());
                t.join();
                System.out.println("Thread " + t.getName() + " joined.");
            }
        }
        mailThreadGroup.destroy();
        System.out.println("Done!");

    }
}

The output:

Calling sendMail().
sendMail() returned.
callSendmailThread joined. Waiting for rest of ThreadGroup to finish.
Joining thread Thread-1
MailHelper: Sending mail for 6s
.
.
.
.
.
.
MailHelper: Sent mail!
Thread Thread-1 joined.
Done!

Note that you must ensure that the Thread-1 is actually started by the time you enumerate the ThreadGroup, thus joining the callSendMailThread is absolutely necessary. Otherwise you'd get a race condition.

Also note that the quirky behaviour of ThreadGroup.enumerate() must be accounted for, by retrying to enumerate all the items several times.

Community
  • 1
  • 1
jmiserez
  • 2,991
  • 1
  • 23
  • 34
0

The easy way to solve the issue logically is to track if the mail is send sucessfully. It can be done by any of the below

1) Set a global variable with some value after mail is send from other thread and reset it value once the attachement is deleted.
2) Instead of variable you can also try creating a file.


Thank you,
Mukeshkoshym

MukeshKoshyM
  • 514
  • 1
  • 8
  • 16