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
015package org.apache.hivemind.service.impl;
016
017import java.util.Iterator;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021import org.apache.hivemind.service.ThreadCleanupListener;
022import org.apache.hivemind.service.ThreadEventNotifier;
023import org.apache.hivemind.util.Defense;
024import 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 */
032public 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}