1 package com.atlassian.plugin.servlet;
2
3 import static com.atlassian.plugin.servlet.descriptors.ServletFilterModuleDescriptor.byWeight;
4
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.Comparator;
8 import java.util.Enumeration;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.LinkedList;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.concurrent.ConcurrentMap;
17
18 import javax.servlet.Filter;
19 import javax.servlet.FilterConfig;
20 import javax.servlet.ServletConfig;
21 import javax.servlet.ServletContext;
22 import javax.servlet.ServletContextEvent;
23 import javax.servlet.ServletException;
24 import javax.servlet.http.HttpServlet;
25
26 import com.atlassian.plugin.ModuleDescriptor;
27 import com.atlassian.plugin.Plugin;
28 import com.atlassian.plugin.event.PluginEventListener;
29 import com.atlassian.plugin.event.PluginEventManager;
30 import com.atlassian.plugin.event.events.PluginDisabledEvent;
31 import com.atlassian.plugin.servlet.descriptors.ServletContextListenerModuleDescriptor;
32 import com.atlassian.plugin.servlet.descriptors.ServletContextParamModuleDescriptor;
33 import com.atlassian.plugin.servlet.descriptors.ServletFilterModuleDescriptor;
34 import com.atlassian.plugin.servlet.descriptors.ServletModuleDescriptor;
35 import com.atlassian.plugin.servlet.filter.DelegatingPluginFilter;
36 import com.atlassian.plugin.servlet.filter.FilterLocation;
37 import com.atlassian.plugin.servlet.filter.PluginFilterConfig;
38 import com.atlassian.plugin.servlet.util.ClassLoaderStack;
39 import com.atlassian.plugin.servlet.util.DefaultPathMapper;
40 import com.atlassian.plugin.servlet.util.LazyLoadedReference;
41 import com.atlassian.plugin.servlet.util.PathMapper;
42
43
44
45
46
47
48 public class DefaultServletModuleManager implements ServletModuleManager
49 {
50 PathMapper servletMapper = new DefaultPathMapper();
51 Map<String, ServletModuleDescriptor> servletDescriptors = new HashMap<String, ServletModuleDescriptor>();
52 ConcurrentMap<String, LazyLoadedReference<HttpServlet>> servletRefs = new ConcurrentHashMap<String, LazyLoadedReference<HttpServlet>>();
53
54 PathMapper filterMapper = new DefaultPathMapper();
55 Map<String, ServletFilterModuleDescriptor> filterDescriptors = new HashMap<String, ServletFilterModuleDescriptor>();
56 ConcurrentMap<String, LazyLoadedReference<Filter>> filterRefs = new ConcurrentHashMap<String, LazyLoadedReference<Filter>>();
57
58 ConcurrentMap<Plugin, LazyLoadedReference<ServletContext>> pluginContextRefs = new ConcurrentHashMap<Plugin, LazyLoadedReference<ServletContext>>();
59
60 public DefaultServletModuleManager(PluginEventManager pluginEventManager)
61 {
62 pluginEventManager.register(this);
63 }
64
65
66
67
68 public void addServletModule(ServletModuleDescriptor descriptor)
69 {
70 servletDescriptors.put(descriptor.getCompleteKey(), descriptor);
71
72 for (String path : descriptor.getPaths())
73 {
74 servletMapper.put(descriptor.getCompleteKey(), path);
75 }
76 }
77
78
79
80
81 public HttpServlet getServlet(String path, final ServletConfig servletConfig) throws ServletException
82 {
83 String completeKey = servletMapper.get(path);
84
85 if (completeKey == null)
86 {
87 return null;
88 }
89 ServletModuleDescriptor descriptor = servletDescriptors.get(completeKey);
90 if (descriptor == null)
91 {
92 return null;
93 }
94
95 return getServlet(descriptor, servletConfig);
96 }
97
98
99
100
101 public void removeServletModule(ServletModuleDescriptor descriptor)
102 {
103 servletDescriptors.remove(descriptor.getCompleteKey());
104 servletMapper.put(descriptor.getCompleteKey(), null);
105
106 LazyLoadedReference<HttpServlet> servletRef = servletRefs.remove(descriptor.getCompleteKey());
107 if (servletRef != null)
108 {
109 servletRef.get().destroy();
110 }
111 }
112
113 public void addFilterModule(ServletFilterModuleDescriptor descriptor)
114 {
115 filterDescriptors.put(descriptor.getCompleteKey(), descriptor);
116
117 for (String path : descriptor.getPaths())
118 {
119 filterMapper.put(descriptor.getCompleteKey(), path);
120 }
121 }
122
123
124
125
126 public Iterable<Filter> getFilters(FilterLocation location, String path, final FilterConfig filterConfig) throws ServletException
127 {
128 List<ServletFilterModuleDescriptor> matchingFilterDescriptors = new ArrayList<ServletFilterModuleDescriptor>();
129 for (String completeKey : filterMapper.getAll(path))
130 {
131 final ServletFilterModuleDescriptor descriptor = filterDescriptors.get(completeKey);
132 if (location.equals(descriptor.getLocation()))
133 {
134 sortedInsert(matchingFilterDescriptors, descriptor, byWeight);
135 }
136 }
137 List<Filter> filters = new LinkedList<Filter>();
138 for (final ServletFilterModuleDescriptor descriptor : matchingFilterDescriptors)
139 {
140 filters.add(getFilter(descriptor, filterConfig));
141 }
142 return filters;
143 }
144
145 static <T> void sortedInsert(List<T> list, final T e, Comparator<T> comparator)
146 {
147 int insertIndex = Collections.binarySearch(list, e, comparator);
148 if (insertIndex < 0)
149 {
150
151 insertIndex = -insertIndex - 1;
152 }
153 else
154 {
155
156 while (insertIndex < list.size() && comparator.compare(list.get(insertIndex), e) == 0)
157 {
158 insertIndex++;
159 }
160 }
161 list.add(insertIndex, e);
162 }
163
164 public void removeFilterModule(ServletFilterModuleDescriptor descriptor)
165 {
166 filterDescriptors.remove(descriptor.getCompleteKey());
167 filterMapper.put(descriptor.getCompleteKey(), null);
168
169 LazyLoadedReference<Filter> filterRef = filterRefs.remove(descriptor.getCompleteKey());
170 if (filterRef != null)
171 {
172 filterRef.get().destroy();
173 }
174 }
175
176
177
178
179
180 @PluginEventListener
181 public void onPluginDisabled(PluginDisabledEvent event)
182 {
183 Plugin plugin = event.getPlugin();
184 LazyLoadedReference<ServletContext> context = pluginContextRefs.remove(plugin);
185 if (context == null)
186 {
187 return;
188 }
189
190 for (ServletContextListenerModuleDescriptor descriptor : findModuleDescriptorsByType(ServletContextListenerModuleDescriptor.class, plugin))
191 {
192 descriptor.getModule().contextDestroyed(new ServletContextEvent(context.get()));
193 }
194 }
195
196
197
198
199
200
201
202
203
204
205
206
207 private HttpServlet getServlet(final ServletModuleDescriptor descriptor, final ServletConfig servletConfig)
208 {
209
210
211 LazyLoadedReference<HttpServlet> servletRef = servletRefs.get(descriptor.getCompleteKey());
212 if (servletRef == null)
213 {
214
215 ServletContext servletContext = getWrappedContext(descriptor.getPlugin(), servletConfig.getServletContext());
216 servletRef = new LazyLoadedServletReference(descriptor, servletContext);
217
218
219
220 if (servletRefs.putIfAbsent(descriptor.getCompleteKey(), servletRef) != null)
221 {
222 servletRef = servletRefs.get(descriptor.getCompleteKey());
223 }
224 }
225 return servletRef.get();
226 }
227
228
229
230
231
232
233
234
235
236
237
238
239 private Filter getFilter(final ServletFilterModuleDescriptor descriptor, final FilterConfig filterConfig)
240 {
241
242
243 LazyLoadedReference<Filter> filterRef = filterRefs.get(descriptor.getCompleteKey());
244 if (filterRef == null)
245 {
246
247 ServletContext servletContext = getWrappedContext(descriptor.getPlugin(), filterConfig.getServletContext());
248 filterRef = new LazyLoadedFilterReference(descriptor, servletContext);
249
250
251
252 if (filterRefs.putIfAbsent(descriptor.getCompleteKey(), filterRef) != null)
253 {
254 filterRef = filterRefs.get(descriptor.getCompleteKey());
255 }
256 }
257 return filterRef.get();
258 }
259
260
261
262
263
264
265
266
267
268
269
270
271
272 private ServletContext getWrappedContext(Plugin plugin, ServletContext baseContext)
273 {
274 LazyLoadedReference<ServletContext> pluginContextRef = pluginContextRefs.get(plugin);
275 if (pluginContextRef == null)
276 {
277 pluginContextRef = new LazyLoadedContextReference(plugin, baseContext);
278 if (pluginContextRefs.putIfAbsent(plugin, pluginContextRef) != null)
279 {
280 pluginContextRef = pluginContextRefs.get(plugin);
281 }
282 }
283 return pluginContextRef.get();
284 }
285
286 private static final class LazyLoadedFilterReference extends LazyLoadedReference<Filter>
287 {
288 private final ServletFilterModuleDescriptor descriptor;
289 private final ServletContext servletContext;
290
291 private LazyLoadedFilterReference(ServletFilterModuleDescriptor descriptor, ServletContext servletContext)
292 {
293 this.descriptor = descriptor;
294 this.servletContext = servletContext;
295 }
296
297 @Override
298 protected Filter create() throws Exception
299 {
300 Filter filter = new DelegatingPluginFilter(descriptor);
301 filter.init(new PluginFilterConfig(descriptor, servletContext));
302 return filter;
303 }
304 }
305
306 private static final class LazyLoadedServletReference extends LazyLoadedReference<HttpServlet>
307 {
308 private final ServletModuleDescriptor descriptor;
309 private final ServletContext servletContext;
310
311 private LazyLoadedServletReference(ServletModuleDescriptor descriptor, ServletContext servletContext)
312 {
313 this.descriptor = descriptor;
314 this.servletContext = servletContext;
315 }
316
317 @Override
318 protected HttpServlet create() throws Exception
319 {
320 HttpServlet servlet = new DelegatingPluginServlet(descriptor);
321 servlet.init(new PluginServletConfig(descriptor, servletContext));
322 return servlet;
323 }
324 }
325
326 private static final class LazyLoadedContextReference extends LazyLoadedReference<ServletContext>
327 {
328 private final Plugin plugin;
329 private final ServletContext baseContext;
330
331 private LazyLoadedContextReference(Plugin plugin, ServletContext baseContext)
332 {
333 this.plugin = plugin;
334 this.baseContext = baseContext;
335 }
336
337 @Override
338 protected ServletContext create() throws Exception
339 {
340 ConcurrentMap<String, Object> contextAttributes = new ConcurrentHashMap<String, Object>();
341 Map<String, String> initParams = mergeInitParams(baseContext, plugin);
342 ServletContext context = new PluginServletContextWrapper(plugin, baseContext, contextAttributes, initParams);
343
344 ClassLoaderStack.push(plugin.getClassLoader());
345 try
346 {
347 for (ServletContextListenerModuleDescriptor descriptor : findModuleDescriptorsByType(ServletContextListenerModuleDescriptor.class, plugin))
348 {
349 descriptor.getModule().contextInitialized(new ServletContextEvent(context));
350 }
351 }
352 finally
353 {
354 ClassLoaderStack.pop();
355 }
356
357 return context;
358 }
359
360 private Map<String, String> mergeInitParams(ServletContext baseContext, Plugin plugin)
361 {
362 Map<String, String> mergedInitParams = new HashMap<String, String>();
363 for (Enumeration<String> e = baseContext.getInitParameterNames(); e.hasMoreElements(); )
364 {
365 String paramName = e.nextElement();
366 mergedInitParams.put(paramName, baseContext.getInitParameter(paramName));
367 }
368 for (ServletContextParamModuleDescriptor descriptor : findModuleDescriptorsByType(ServletContextParamModuleDescriptor.class, plugin))
369 {
370 mergedInitParams.put(descriptor.getParamName(), descriptor.getParamValue());
371 }
372 return Collections.unmodifiableMap(mergedInitParams);
373 }
374 }
375
376 static <T extends ModuleDescriptor<?>> Iterable<T> findModuleDescriptorsByType(Class<T> type, Plugin plugin)
377 {
378 Set<T> descriptors = new HashSet<T>();
379 for (ModuleDescriptor<?> descriptor : plugin.getModuleDescriptors())
380 {
381 if (type.isAssignableFrom(descriptor.getClass()))
382 {
383 descriptors.add((T) descriptor);
384 }
385 }
386 return descriptors;
387 }
388 }