Thread keeps running even after application has been stopped in Websphere
Asked Answered
K

1

7

I have a long running thread which is created using org.springframework.scheduling.commonj.WorkManagerTaskExecutor with Spring and is running in Websphere Application Server 8.

The problem is that this thread keeps running even if the application has been stopped. That thread needs to be stopped too but it is not happening. I even tried to use Thread.currentThread().isInterrupted() to check if the current thread was interrupted but it always returns false. So there is no way to know through my code if the Thread should keep running or stop.

This is my spring configuration for the WorkManagerTaskExecutor:

<bean id="taskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
      <property name="workManagerName" value="wm/default" />
</bean>

The thread is being executed this way:

Thread t = new EmailReaderThread(email);
workManagerTaskExecutor.execute(t);
  • What am I missing?
  • What can I do so that whenever the application is stopped the application's thread (threads that were generated by the application) stops too?

I think this is not considered an unmanaged thread because I am registering the thread using the appropriate WorkManager which the container exposes as a resource by JNDI.

Update: Here is the code which creates the Thread.

@Service
@Transactional
public class SmsServiceHypermedia implements SmsService {

    @Autowired
    private WorkManagerTaskExecutor workManagerTaskExecutor;


    public SmsServiceHypermedia() {
        createEmailReaderThread();
    }

    private void createEmailReaderThread() {
        log.debug("Generating Email Reader Threads...");
        Email email = getDefaultEmail(); //obtain the default Email object, not important for the problem.
        EmailReaderThread r = new EmailReaderThread(email);
        workManagerTaskExecutor.execute(r);     
    }

    private class EmailReaderThread extends Thread {

        private Email email;
        private Session session;

        public EmailReaderThread(Email email) {
            this.email = email;
        }

        @Override
        public void run()  {
            readEmails();
        }

        public void readEmails() {
            final long delay = 30 * 1000; //delay between message poll.
            log.debug("Starting to read emails for email: " + email.getAddress());
            while(!Thread.currentThread().isInterrupted()) {
                try {
                    log.debug("Current session: " + session);
                    Store store = session.getStore();
                    log.debug("Connecting using session: " + session);
                    store.connect();
                    Folder inbox = store.getFolder("INBOX");
                    inbox.open(Folder.READ_WRITE);

                    javax.mail.Message[] messages = inbox.search(
                            new FlagTerm(new Flags(Flags.Flag.SEEN), false));
                    for (javax.mail.Message message : messages) {
                        //Do something with the message
                    }
                    inbox.close(true);
                    store.close();
                    block(delay);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }

        //I know this could be implemented by calling Thread.sleep() is just that I ran out of options so I also tried it this way.
        private void block(long millis) {
            final long endTime = System.currentTimeMillis() + millis;
            log.debug("Blocking for this amount of time: " + millis + " ms");
            while (System.currentTimeMillis() < endTime) {
            }
            log.debug("End of blocking.");
        }
    }   
}
Knobby answered 28/12, 2011 at 18:11 Comment(4)
Shouldn't you be passing a Runnable to WorkManagerTaskExecutor.execute(WorkManagerTaskExecutor task) instead of a Thread?Chagres
A Thread implements Runnable so is a Runnable. I think that's not the problem.Knobby
executors never call the start() method of a passed in thread (only the run()) so you might as well use a normal runnable (not a thread)Oisin
Yeah because of that the executed code must be in the run() method. Check my updated code.Knobby
O
6

According to the CommonJ specs, a WorkManager will attempt to stop the execution of a Work only if its isDaemon() method returns true. Non daemon Works are expected to be short running so that they don't need to be stopped.

The problem is that by default, the isDaemon() method of the Work implementation used by Spring (and which actually wraps the Runnable) returns false. You can change that by making your Runnable implement SchedulingAwareRunnable.

However, that is not enough. If the WorkManager decides to stop the Work, then it will call Work#release() and it is the responsibility of the Work itself to make sure that it stops. In particular, the WorkManager will not attempt to interrupt the thread that is executing the Work (because that is not a reliable way to stop a thread). The problem is that the Work implementation used by Spring has an empty implementation for the release() method, so that you can't use that feature.

To summarize: if you want to use Spring, the only way to make sure that the execution is stopped is to design your own mechanism for that.

Note that it is still interesting to use SchedulingAwareRunnable, because this will avoid the warnings generated by WebSphere's thread monitor (about hanging threads).

Olivas answered 29/12, 2011 at 19:59 Comment(1)
I checked the source code of WorkManagerTaskExecutor and what you wrote makes perfect sense. Thank you. I think it is very bad that you can't override the "release()" with your own implementation.Knobby

© 2022 - 2024 — McMap. All rights reserved.