You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by ha...@apache.org on 2004/03/01 16:32:40 UTC

cvs commit: jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/hivemind/service/impl ThreadLocalStorageImpl.java

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 {@link 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 {@link 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 {@link ThreadEventNotifier}.
  +         * <p>
  +         * The registration cannot be done from here because it may get lost once the caller method (
  +         * {@link ThreadLocal#get()} or {@link 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 {@link ThreadEventNotifier}
  +     * if the thread local variable has been initialized. The registration cannot be done from
  +     * within {@link 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 {@link ThreadEventNotifier} as a {@link ThreadCleanupListener}.
  -     */
  -    public void initializeService()
  -    {
  -        _notifier.addThreadCleanupListener(this);
       }
   
       /**
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org