On Fri, Jul 4, 2008 at 6:33 AM, Jerome Louvel <[EMAIL PROTECTED]> wrote:

> Tim, instead of wrapping the submitted tasks with thread-local copy/reset
> code, I've chosen to use the ability to provide a ThreadFactory to the
> Executors static creation method.
>
> Do you see any drawback compared to your suggested approach?
>

Yes. :-(

The run method of a thread returned by ThreadFactory is not called once per
task, but only once when the thread is first added to the pool. So pool
thread A will "burn in" the current values of whatever thread is current
when thread A is added to the pool. But thread A in general is used to
handle multiple tasks, each with different current Applications, Contexts,
VirtualHosts, and Responses, for which the burned in values will be
incorrect.

I recommend replacing Context.getExecutorService() with
Context.wrapExecutorService(ExecutorService exec) as follows:

    public ExecutorService wrapExecutorService(final ExecutorService exec) {
        return new AbstractExecutorService() {
            public void execute(final Runnable runnable) {

                final Application currentApplication =
Application.getCurrent();
                final Context currentContext = Context.getCurrent();
                final Integer currentVirtualHost = VirtualHost.getCurrent();
                final Response currentResponse = Response.getCurrent();

                execute(new Runnable() {
                    public void run() {
                        // Copy the thread local variables
                        Response.setCurrent(currentResponse);
                        Context.setCurrent(currentContext);
                        VirtualHost.setCurrent(currentVirtualHost);
                        Application.setCurrent(currentApplication);

                        try {
                            // Run the user task
                            runnable.run();
                        } finally {
                            // Reset the thread local variables
                            Response.setCurrent(null);
                            Context.setCurrent(null);
                            VirtualHost.setCurrent(-1);
                            Application.setCurrent(null);
                        }
                    }
                });
            }
            public boolean isShutdown() { return exec.isShutdown(); }
            public boolean isTerminated() { return exec.isTerminated(); }
            public void shutdown() { exec.shutdown(); }
            public List<Runnable> shutdownNow() { return exec.shutdownNow();
}
            public boolean awaitTermination(long timeout, TimeUnit unit)
                    throws InterruptedException {
                return exec.awaitTermination(timeout, unit);
            }
        };
    }

If you really want to keep Context.getExecutorService, change
createExecutorService to return
wrapExecutorService(Executors.newCachedThreadPool());

--tim

Reply via email to