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 }