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 }