001 // Copyright 2006, 2007, 2008, 2010, 2011 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.tapestry5.ioc.internal.services; 016 017 import org.apache.tapestry5.ioc.Invokable; 018 import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 019 import org.apache.tapestry5.ioc.internal.util.JDKUtils; 020 import org.apache.tapestry5.ioc.services.PerThreadValue; 021 import org.apache.tapestry5.ioc.services.PerthreadManager; 022 import org.apache.tapestry5.ioc.services.ThreadCleanupListener; 023 import org.slf4j.Logger; 024 025 import java.util.List; 026 import java.util.Map; 027 import java.util.concurrent.atomic.AtomicInteger; 028 import java.util.concurrent.locks.Lock; 029 030 @SuppressWarnings("all") 031 public class PerthreadManagerImpl implements PerthreadManager 032 { 033 private final Lock lock = JDKUtils.createLockForThreadLocalCreation(); 034 035 private final PerThreadValue<List<ThreadCleanupListener>> listenersValue; 036 037 private static class MapHolder extends ThreadLocal<Map> 038 { 039 @Override 040 protected Map initialValue() 041 { 042 return CollectionFactory.newMap(); 043 } 044 } 045 046 private final Logger logger; 047 048 private final MapHolder holder = new MapHolder(); 049 050 private final AtomicInteger uuidGenerator = new AtomicInteger(); 051 052 public PerthreadManagerImpl(Logger logger) 053 { 054 this.logger = logger; 055 056 listenersValue = createValue(); 057 } 058 059 private Map getPerthreadMap() 060 { 061 lock.lock(); 062 063 try 064 { 065 return holder.get(); 066 } finally 067 { 068 lock.unlock(); 069 } 070 } 071 072 private List<ThreadCleanupListener> getListeners() 073 { 074 List<ThreadCleanupListener> result = listenersValue.get(); 075 076 if (result == null) 077 { 078 result = CollectionFactory.newList(); 079 listenersValue.set(result); 080 } 081 082 return result; 083 } 084 085 public void addThreadCleanupListener(ThreadCleanupListener listener) 086 { 087 getListeners().add(listener); 088 } 089 090 /** 091 * Instructs the hub to notify all its listeners (for the current thread). 092 * It also discards its list of listeners. 093 */ 094 public void cleanup() 095 { 096 List<ThreadCleanupListener> listeners = getListeners(); 097 098 listenersValue.set(null); 099 100 for (ThreadCleanupListener listener : listeners) 101 { 102 try 103 { 104 listener.threadDidCleanup(); 105 } catch (Exception ex) 106 { 107 logger.warn(ServiceMessages.threadCleanupError(listener, ex), ex); 108 } 109 } 110 111 // Listeners should not re-add themselves or store any per-thread state 112 // here, it will be lost. 113 114 try 115 { 116 lock.lock(); 117 118 // Discard the per-thread map of values, including the key that stores 119 // the listeners. This means that if a listener attempts to register 120 // new listeners, the new listeners will not be triggered and will be 121 // released to the GC. 122 123 holder.remove(); 124 } finally 125 { 126 lock.unlock(); 127 } 128 } 129 130 private static Object NULL_VALUE = new Object(); 131 132 <T> PerThreadValue<T> createValue(final Object key) 133 { 134 return new PerThreadValue<T>() 135 { 136 public T get() 137 { 138 return get(null); 139 } 140 141 public T get(T defaultValue) 142 { 143 Map map = getPerthreadMap(); 144 145 if (map.containsKey(key)) 146 { 147 Object storedValue = map.get(key); 148 149 if (storedValue == NULL_VALUE) 150 return null; 151 152 return (T) storedValue; 153 } 154 155 return defaultValue; 156 } 157 158 public T set(T newValue) 159 { 160 getPerthreadMap().put(key, newValue == null ? NULL_VALUE : newValue); 161 162 return newValue; 163 } 164 165 public boolean exists() 166 { 167 return getPerthreadMap().containsKey(key); 168 } 169 }; 170 } 171 172 public <T> PerThreadValue<T> createValue() 173 { 174 return createValue(uuidGenerator.getAndIncrement()); 175 } 176 177 public void run(Runnable runnable) 178 { 179 assert runnable != null; 180 181 try 182 { 183 runnable.run(); 184 } finally 185 { 186 cleanup(); 187 } 188 } 189 190 public <T> T invoke(Invokable<T> invokable) 191 { 192 try 193 { 194 return invokable.invoke(); 195 } finally 196 { 197 cleanup(); 198 } 199 } 200 }