001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.hivemind.service.impl;
016    
017    import java.util.Iterator;
018    
019    import org.apache.commons.logging.Log;
020    import org.apache.commons.logging.LogFactory;
021    import org.apache.hivemind.service.ThreadCleanupListener;
022    import org.apache.hivemind.service.ThreadEventNotifier;
023    import org.apache.hivemind.util.Defense;
024    import org.apache.hivemind.util.EventListenerList;
025    
026    /**
027     * Implementation of {@link org.apache.hivemind.service.ThreadEventNotifier}, available as service
028     * <code>hivemind.ThreadEventNotifier</code>.
029     * 
030     * @author Howard Lewis Ship
031     */
032    public class ThreadEventNotifierImpl implements ThreadEventNotifier
033    {
034        private static final Log DEFAULT_LOG = LogFactory.getLog(ThreadEventNotifier.class);
035    
036        private final Log _log;
037    
038        private final ThreadLocal _storage = new ThreadLocal();
039    
040        public ThreadEventNotifierImpl()
041        {
042            this(DEFAULT_LOG);
043        }
044    
045        public ThreadEventNotifierImpl(Log log)
046        {
047            Defense.notNull(log, "log");
048    
049            _log = log;
050        }
051    
052        public void addThreadCleanupListener(ThreadCleanupListener listener)
053        {
054            EventListenerList list = (EventListenerList) _storage.get();
055    
056            if (list == null)
057            {
058                list = new EventListenerList();
059                _storage.set(list);
060            }
061    
062            list.addListener(listener);
063        }
064    
065        public void removeThreadCleanupListener(ThreadCleanupListener listener)
066        {
067            EventListenerList list = (EventListenerList) _storage.get();
068    
069            if (list != null)
070                list.removeListener(listener);
071        }
072    
073        public void fireThreadCleanup()
074        {
075            // Here's where we need the CursorableLinkedList since listeners
076            // are free to unregister as listeners from threadDidCleanup() and
077            // we need to avoid concurrent modification errors.
078    
079            EventListenerList list = (EventListenerList) _storage.get();
080    
081            if (list == null)
082                return;
083    
084            Iterator i = list.getListeners();
085    
086            // Discard the list of listeners as early as possible to ensure that
087            // they can in no way be retained, even if this thread aborts abnormally.
088    
089            _storage.set(null);
090    
091            while (i.hasNext())
092            {
093                ThreadCleanupListener listener = (ThreadCleanupListener) i.next();
094    
095                // Each listener may decide to remove itself; that's OK,
096                // EventListenerList handles that kind of concurrent modification
097                // well.
098    
099                try
100                {
101                    listener.threadDidCleanup();
102                }
103                catch (RuntimeException ex)
104                {
105                    _log.warn(ServiceMessages.threadCleanupException(ex), ex);
106                }
107            }
108        }
109    
110    }