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 }