harishkswamy 2004/03/01 07:32:39 Modified: hivemind/framework/src/test/hivemind/test/services TestThreadLocalStorage.java hivemind/framework/src/java/org/apache/hivemind/service/impl ThreadLocalStorageImpl.java Log: Fix ThreadCleanupListener registration. Revision Changes Path 1.6 +72 -7 jakarta-commons-sandbox/hivemind/framework/src/test/hivemind/test/services/TestThreadLocalStorage.java Index: TestThreadLocalStorage.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/hivemind/framework/src/test/hivemind/test/services/TestThreadLocalStorage.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- TestThreadLocalStorage.java 26 Feb 2004 23:07:36 -0000 1.5 +++ TestThreadLocalStorage.java 1 Mar 2004 15:32:39 -0000 1.6 @@ -1,4 +1,4 @@ -// Copyright 2004 The Apache Software Foundation +// Copyright 2004 The Apache Software Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,22 +14,24 @@ package hivemind.test.services; +import hivemind.test.FrameworkTestCase; + import org.apache.hivemind.service.ThreadEventNotifier; import org.apache.hivemind.service.ThreadLocalStorage; import org.apache.hivemind.service.impl.ThreadEventNotifierImpl; import org.apache.hivemind.service.impl.ThreadLocalStorageImpl; -import hivemind.test.FrameworkTestCase; - /** * Tests for [EMAIL PROTECTED] org.apache.hivemind.service.impl.ThreadLocalStorageImpl}. - * - * @author Howard Lewis Ship + * + * @author Howard Lewis Ship, Harish Krishnaswamy * @version $Id$ */ public class TestThreadLocalStorage extends FrameworkTestCase { private ThreadLocalStorage _s = new ThreadLocalStorageImpl(); + private Throwable _testRunnerFailure; + private boolean _testRunnerCompleted; public void testGetEmpty() { @@ -67,7 +69,6 @@ ThreadLocalStorageImpl s = new ThreadLocalStorageImpl(); s.setNotifier(notifier); - s.initializeService(); s.put("biff", "bamf"); @@ -78,4 +79,68 @@ assertNull(s.get("biff")); } + private class TestRunner implements Runnable + { + private ThreadLocalStorage _local; + private ThreadEventNotifier _notifier; + + private TestRunner(ThreadLocalStorage local, ThreadEventNotifier notifier) + { + _local = local; + _notifier = notifier; + } + + public void run() + { + _local.put("session", "Test Runner Session"); + + assertEquals(_local.get("session"), "Test Runner Session"); + + _notifier.fireThreadCleanup(); + + assertNull(_local.get("session")); + + _testRunnerCompleted = true; + } + } + + private class TestThreadGroup extends ThreadGroup + { + public TestThreadGroup(String name) + { + super(name); + } + + public void uncaughtException(Thread th, Throwable t) + { + _testRunnerFailure = t; + _testRunnerCompleted = true; + } + } + + public void testThreadCleanup() throws Throwable + { + ThreadEventNotifier notifier = new ThreadEventNotifierImpl(); + ThreadLocalStorageImpl local = new ThreadLocalStorageImpl(); + + local.setNotifier(notifier); + + local.put("session", "Main Session"); + + TestRunner tr = new TestRunner(local, notifier); + TestThreadGroup tg = new TestThreadGroup("Test Thread Group"); + new Thread(tg, tr).start(); + + while (!_testRunnerCompleted) + Thread.yield(); + + if (_testRunnerFailure != null) + throw _testRunnerFailure; + + assertEquals(local.get("session"), "Main Session"); + + notifier.fireThreadCleanup(); + + assertNull(local.get("session")); + } } 1.2 +55 -27 jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/hivemind/service/impl/ThreadLocalStorageImpl.java Index: ThreadLocalStorageImpl.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/hivemind/service/impl/ThreadLocalStorageImpl.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- ThreadLocalStorageImpl.java 26 Feb 2004 23:07:45 -0000 1.1 +++ ThreadLocalStorageImpl.java 1 Mar 2004 15:32:39 -0000 1.2 @@ -1,4 +1,4 @@ -// Copyright 2004 The Apache Software Foundation +// Copyright 2004 The Apache Software Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,41 +20,78 @@ import org.apache.hivemind.service.ThreadCleanupListener; import org.apache.hivemind.service.ThreadEventNotifier; import org.apache.hivemind.service.ThreadLocalStorage; -import org.apache.hivemind.Initializable; /** * Implementation of [EMAIL PROTECTED] org.apache.hivemind.service.ThreadLocalStorage}. - * - * @author Howard Lewis Ship + * + * @author Howard Lewis Ship, Harish Krishnaswamy * @version $Id$ */ -public class ThreadLocalStorageImpl - implements ThreadLocalStorage, Initializable, ThreadCleanupListener +public class ThreadLocalStorageImpl implements ThreadLocalStorage, ThreadCleanupListener { - private ThreadLocal _local = new ThreadLocal(); + private static final String INITIALIZED_KEY = "initialized"; - private ThreadEventNotifier _notifier; + private CleanableThreadLocal _local = new CleanableThreadLocal(); + private ThreadEventNotifier _notifier; - public Object get(String key) + private static class CleanableThreadLocal extends ThreadLocal { - Map map = (Map) _local.get(); + /** + * <p> + * Intializes the variable with a HashMap containing a single Boolean flag to denote the + * initialization of the variable. The Boolean flag will be used to determine when to + * register the listener with [EMAIL PROTECTED] ThreadEventNotifier}. + * <p> + * The registration cannot be done from here because it may get lost once the caller method ( + * [EMAIL PROTECTED] ThreadLocal#get()} or [EMAIL PROTECTED] ThreadLocal#set(java.lang.Object)} completes, if + * this was the first ThreadLocal variable access for the Thread. + */ + protected Object initialValue() + { + // NOTE: This is a workaround to circumvent the ThreadLocal behavior. + // It would be easier if the implementation of ThreadLocal.get() checked for + // the existence of the thread local map, after initialValue() is evaluated, + // and used it instead of creating a new map always after initialization (possibly + // overwriting any variables created from within ThreadLocal.initialValue()). - if (map == null) - return null; + Map map = new HashMap(); + map.put(INITIALIZED_KEY, Boolean.TRUE); - return map.get(key); + return map; + } } - public void put(String key, Object value) + /** + * Gets the thread local variable and registers the listener with [EMAIL PROTECTED] ThreadEventNotifier} + * if the thread local variable has been initialized. The registration cannot be done from + * within [EMAIL PROTECTED] CleanableThreadLocal#initialValue()}because the notifier's thread local + * variable will be overwritten and the listeners for the thread will be lost. + */ + private Map getThreadLocalVariable() { Map map = (Map) _local.get(); - if (map == null) + if (Boolean.TRUE.equals(map.get(INITIALIZED_KEY)) && _notifier != null) { - map = new HashMap(); - _local.set(map); + _notifier.addThreadCleanupListener(this); + + map.remove(INITIALIZED_KEY); } + return map; + } + + public Object get(String key) + { + Map map = getThreadLocalVariable(); + + return map.get(key); + } + + public void put(String key, Object value) + { + Map map = getThreadLocalVariable(); + map.put(key, value); } @@ -69,15 +106,6 @@ public void setNotifier(ThreadEventNotifier notifier) { _notifier = notifier; - } - - /** - * Initializes the service; the implementation registers itself - * with the [EMAIL PROTECTED] ThreadEventNotifier} as a [EMAIL PROTECTED] ThreadCleanupListener}. - */ - public void initializeService() - { - _notifier.addThreadCleanupListener(this); } /**
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]