1 package com.atlassian.plugin.servlet;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collections;
6 import java.util.Enumeration;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.LinkedList;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Set;
13 import java.util.concurrent.ConcurrentHashMap;
14 import java.util.concurrent.ConcurrentMap;
15
16 import javax.servlet.Filter;
17 import javax.servlet.FilterConfig;
18 import javax.servlet.ServletConfig;
19 import javax.servlet.ServletContext;
20 import javax.servlet.ServletContextEvent;
21 import javax.servlet.ServletContextListener;
22 import javax.servlet.ServletException;
23 import javax.servlet.http.HttpServlet;
24
25 import com.atlassian.plugin.ModuleDescriptor;
26 import com.atlassian.plugin.Plugin;
27 import com.atlassian.plugin.descriptors.AbstractModuleDescriptor;
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.event.events.PluginFrameworkShutdownEvent;
32 import com.atlassian.plugin.event.events.PluginFrameworkShuttingDownEvent;
33 import com.atlassian.plugin.servlet.descriptors.ServletContextListenerModuleDescriptor;
34 import com.atlassian.plugin.servlet.descriptors.ServletContextParamModuleDescriptor;
35 import com.atlassian.plugin.servlet.descriptors.ServletFilterModuleDescriptor;
36 import com.atlassian.plugin.servlet.descriptors.ServletModuleDescriptor;
37 import com.atlassian.plugin.servlet.filter.FilterDispatcherCondition;
38 import com.atlassian.plugin.servlet.filter.FilterLocation;
39 import com.atlassian.plugin.servlet.filter.PluginFilterConfig;
40 import com.atlassian.plugin.servlet.util.DefaultPathMapper;
41 import com.atlassian.plugin.servlet.util.PathMapper;
42 import com.atlassian.plugin.servlet.util.ServletContextServletModuleManagerAccessor;
43 import com.atlassian.plugin.util.ClassLoaderStack;
44 import com.atlassian.util.concurrent.LazyReference;
45
46 import com.google.common.base.Objects;
47
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import static com.atlassian.plugin.servlet.descriptors.ServletFilterModuleDescriptor.byWeight;
52 import static com.google.common.base.Preconditions.checkNotNull;
53
54
55
56
57
58
59 public class DefaultServletModuleManager implements ServletModuleManager
60 {
61 private static final Logger log = LoggerFactory.getLogger(DefaultServletModuleManager.class);
62
63 private final PathMapper servletMapper;
64 private final Map<String, ServletModuleDescriptor> servletDescriptors = new ConcurrentHashMap<String, ServletModuleDescriptor>();
65 private final ConcurrentMap<String, LazyReference<HttpServlet>> servletRefs = new ConcurrentHashMap<String, LazyReference<HttpServlet>>();
66
67 private final PathMapper filterMapper;
68 private final Map<String, ServletFilterModuleDescriptor> filterDescriptors = new ConcurrentHashMap<String, ServletFilterModuleDescriptor>();
69 private final ConcurrentMap<String, LazyReference<Filter>> filterRefs = new ConcurrentHashMap<String, LazyReference<Filter>>();
70 private final FilterFactory filterFactory;
71
72 private final ConcurrentMap<Plugin, ContextLifecycleReference> pluginContextRefs = new ConcurrentHashMap<Plugin, ContextLifecycleReference>();
73
74
75
76
77
78
79
80
81 public DefaultServletModuleManager(final ServletContext servletContext, final PluginEventManager pluginEventManager)
82 {
83 this(pluginEventManager);
84 ServletContextServletModuleManagerAccessor.setServletModuleManager(servletContext, this);
85 }
86
87
88
89
90
91
92
93
94
95
96
97 @Deprecated
98 public DefaultServletModuleManager(final PluginEventManager pluginEventManager)
99 {
100 this(pluginEventManager, new DefaultPathMapper(), new DefaultPathMapper());
101 }
102
103
104
105
106
107
108
109
110
111
112
113
114
115 @Deprecated
116 public DefaultServletModuleManager(final PluginEventManager pluginEventManager, final PathMapper servletPathMapper, final PathMapper filterPathMapper)
117 {
118 this(pluginEventManager, servletPathMapper, filterPathMapper, new FilterFactory());
119 }
120
121
122
123
124
125
126
127
128
129
130
131 DefaultServletModuleManager(
132 final PluginEventManager pluginEventManager,
133 final PathMapper servletMapper,
134 final PathMapper filterMapper,
135 final FilterFactory filterFactory
136 )
137 {
138 this.servletMapper = servletMapper;
139 this.filterMapper = filterMapper;
140 this.filterFactory = filterFactory;
141 pluginEventManager.register(this);
142 }
143
144 public void addServletModule(final ServletModuleDescriptor descriptor)
145 {
146 servletDescriptors.put(descriptor.getCompleteKey(), descriptor);
147
148
149
150 final List<String> paths = descriptor.getPaths();
151 for (final String path : paths)
152 {
153 servletMapper.put(descriptor.getCompleteKey(), path);
154 }
155 final LazyReference<HttpServlet> servletRef = servletRefs.remove(descriptor.getCompleteKey());
156 if (servletRef != null)
157 {
158 servletRef.get().destroy();
159 }
160 }
161
162 public HttpServlet getServlet(final String path, final ServletConfig servletConfig) throws ServletException
163 {
164 final String completeKey = servletMapper.get(path);
165
166 if (completeKey == null)
167 {
168 return null;
169 }
170 final ServletModuleDescriptor descriptor = servletDescriptors.get(completeKey);
171 if (descriptor == null)
172 {
173 return null;
174 }
175
176 final HttpServlet servlet = getServlet(descriptor, servletConfig);
177 if (servlet == null)
178 {
179 servletRefs.remove(descriptor.getCompleteKey());
180 }
181 return servlet;
182 }
183
184 public void removeServletModule(final ServletModuleDescriptor descriptor)
185 {
186 servletDescriptors.remove(descriptor.getCompleteKey());
187 servletMapper.put(descriptor.getCompleteKey(), null);
188
189 final LazyReference<HttpServlet> servletRef = servletRefs.remove(descriptor.getCompleteKey());
190 if (servletRef != null)
191 {
192 servletRef.get().destroy();
193 }
194 }
195
196 public void addFilterModule(final ServletFilterModuleDescriptor descriptor)
197 {
198 filterDescriptors.put(descriptor.getCompleteKey(), descriptor);
199
200 for (final String path : descriptor.getPaths())
201 {
202 filterMapper.put(descriptor.getCompleteKey(), path);
203 }
204 final LazyReference<Filter> filterRef = filterRefs.remove(descriptor.getCompleteKey());
205 if (filterRef != null)
206 {
207 filterRef.get().destroy();
208 }
209 }
210
211 public Iterable<Filter> getFilters(final FilterLocation location, final String path, final FilterConfig filterConfig)
212 throws ServletException
213 {
214 return getFilters(location, path, filterConfig, FilterDispatcherCondition.REQUEST);
215 }
216
217 public Iterable<Filter> getFilters(FilterLocation location, String path, FilterConfig filterConfig, FilterDispatcherCondition condition)
218 throws ServletException
219 {
220 checkNotNull(condition);
221 final List<ServletFilterModuleDescriptor> matchingFilterDescriptors = new ArrayList<ServletFilterModuleDescriptor>();
222
223 for (final String completeKey : filterMapper.getAll(path))
224 {
225 final ServletFilterModuleDescriptor descriptor = filterDescriptors.get(completeKey);
226 if (!descriptor.getDispatcherConditions().contains(condition))
227 {
228 if (log.isTraceEnabled())
229 {
230 log.trace("Skipping filter " + descriptor.getCompleteKey() + " as condition " + condition +
231 " doesn't match list:" + Arrays.asList(descriptor.getDispatcherConditions()));
232 }
233 continue;
234 }
235
236 if (location.equals(descriptor.getLocation()))
237 {
238 matchingFilterDescriptors.add(descriptor);
239 }
240 }
241 Collections.sort(matchingFilterDescriptors, byWeight);
242 final List<Filter> filters = new LinkedList<Filter>();
243 for (final ServletFilterModuleDescriptor descriptor : matchingFilterDescriptors)
244 {
245 final Filter filter = getFilter(descriptor, filterConfig);
246 if (filter == null)
247 {
248 filterRefs.remove(descriptor.getCompleteKey());
249 }
250 else
251 {
252 filters.add(filter);
253 }
254 }
255
256 return filters;
257 }
258
259
260 public void removeFilterModule(final ServletFilterModuleDescriptor descriptor)
261 {
262 filterDescriptors.remove(descriptor.getCompleteKey());
263 filterMapper.put(descriptor.getCompleteKey(), null);
264
265 final LazyReference<Filter> filterRef = filterRefs.remove(descriptor.getCompleteKey());
266 if (filterRef != null)
267 {
268 filterRef.get().destroy();
269 }
270 }
271
272
273
274
275
276 @PluginEventListener
277 public void onPluginDisabled(final PluginDisabledEvent event)
278 {
279 final Plugin plugin = event.getPlugin();
280 final ContextLifecycleReference context = pluginContextRefs.remove(plugin);
281 if (context == null)
282 {
283 return;
284 }
285
286 context.get().contextDestroyed();
287 }
288
289 @PluginEventListener
290 public void onPluginFrameworkBeforeShutdown(final PluginFrameworkShuttingDownEvent event)
291 {
292 destroy();
293 }
294
295 private void destroy()
296 {
297 destroyModuleDescriptors(servletDescriptors);
298
299 destroyModuleDescriptors(filterDescriptors);
300
301
302
303 for (final ContextLifecycleReference context : new ArrayList<ContextLifecycleReference>(pluginContextRefs.values()))
304 {
305 if (context != null)
306 {
307 ContextLifecycleManager lifecycleManager = context.get();
308 if (lifecycleManager != null)
309 {
310 lifecycleManager.contextDestroyed();
311 }
312 }
313 }
314 pluginContextRefs.clear();
315 }
316
317 private <T extends ModuleDescriptor> void destroyModuleDescriptors(Map<String, T> descriptors)
318 {
319
320
321
322 for (final ModuleDescriptor moduleDescriptor : new ArrayList<T>(descriptors.values()))
323 {
324 if (moduleDescriptor != null)
325 {
326 moduleDescriptor.destroy();
327 }
328 }
329 descriptors.clear();
330 }
331
332 @Deprecated
333 public void onPluginFrameworkShutdown(final PluginFrameworkShutdownEvent event)
334 {
335 destroy();
336 }
337
338
339
340
341
342
343
344
345 HttpServlet getServlet(final ServletModuleDescriptor descriptor, final ServletConfig servletConfig)
346 {
347 return getInstance(servletRefs, descriptor, new LazyLoadedServletReference(descriptor, servletConfig));
348
349 }
350
351
352
353
354
355
356
357
358
359
360 Filter getFilter(final ServletFilterModuleDescriptor descriptor, final FilterConfig filterConfig)
361 {
362 return getInstance(filterRefs, descriptor, new LazyLoadedFilterReference(descriptor, filterConfig));
363 }
364
365 private <T> T getInstance(ConcurrentMap<String, LazyReference<T>> refs, AbstractModuleDescriptor descriptor,
366 LazyReference<T> newRef)
367 {
368 try
369 {
370 final LazyReference<T> oldRef = refs.putIfAbsent(descriptor.getCompleteKey(), newRef);
371 return oldRef != null ? oldRef.get() : newRef.get();
372 }
373 catch (final RuntimeException ex)
374 {
375 log.error("Unable to create new reference " + newRef, ex);
376 return null;
377 }
378
379 }
380
381
382
383
384
385
386
387
388
389
390
391
392 private ServletContext getWrappedContext(final Plugin plugin, final ServletContext baseContext)
393 {
394 ContextLifecycleReference pluginContextRef = pluginContextRefs.get(plugin);
395 if (pluginContextRef == null)
396 {
397 pluginContextRef = new ContextLifecycleReference(plugin, baseContext);
398 if (pluginContextRefs.putIfAbsent(plugin, pluginContextRef) != null)
399 {
400 pluginContextRef = pluginContextRefs.get(plugin);
401 }
402 }
403 return pluginContextRef.get().servletContext;
404 }
405
406 private final class LazyLoadedFilterReference extends LazyReference<Filter>
407 {
408 private final ServletFilterModuleDescriptor descriptor;
409 private final FilterConfig filterConfig;
410
411 private LazyLoadedFilterReference(final ServletFilterModuleDescriptor descriptor, final FilterConfig filterConfig)
412 {
413 this.descriptor = descriptor;
414 this.filterConfig = filterConfig;
415 }
416
417 @Override
418 protected Filter create() throws Exception
419 {
420 final Filter filter = filterFactory.newFilter(descriptor);
421 final ServletContext servletContext = getWrappedContext(descriptor.getPlugin(), filterConfig.getServletContext());
422 filter.init(new PluginFilterConfig(descriptor, servletContext));
423 return filter;
424 }
425
426 @Override
427 public String toString()
428 {
429 return Objects.toStringHelper(this)
430 .add("descriptor", descriptor)
431 .add("filterConfig", filterConfig)
432 .toString();
433 }
434 }
435
436 private final class LazyLoadedServletReference extends LazyReference<HttpServlet>
437 {
438 private final ServletModuleDescriptor descriptor;
439 private final ServletConfig servletConfig;
440
441 private LazyLoadedServletReference(final ServletModuleDescriptor descriptor, final ServletConfig servletConfig)
442 {
443 this.descriptor = descriptor;
444 this.servletConfig = servletConfig;
445 }
446
447 @Override
448 protected HttpServlet create() throws Exception
449 {
450 final HttpServlet servlet = new DelegatingPluginServlet(descriptor);
451 final ServletContext servletContext = getWrappedContext(descriptor.getPlugin(), servletConfig.getServletContext());
452 servlet.init(new PluginServletConfig(descriptor, servletContext));
453 return servlet;
454 }
455
456 @Override
457 public String toString()
458 {
459 return Objects.toStringHelper(this)
460 .add("descriptor", descriptor)
461 .add("servletConfig", servletConfig)
462 .toString();
463 }
464 }
465
466 private static final class ContextLifecycleReference extends LazyReference<ContextLifecycleManager>
467 {
468 private final Plugin plugin;
469 private final ServletContext baseContext;
470
471 private ContextLifecycleReference(final Plugin plugin, final ServletContext baseContext)
472 {
473 this.plugin = plugin;
474 this.baseContext = baseContext;
475 }
476
477 @Override
478 protected ContextLifecycleManager create() throws Exception
479 {
480 final ConcurrentMap<String, Object> contextAttributes = new ConcurrentHashMap<String, Object>();
481 final Map<String, String> initParams = mergeInitParams(baseContext, plugin);
482 final ServletContext context = new PluginServletContextWrapper(plugin, baseContext, contextAttributes, initParams);
483
484 ClassLoaderStack.push(plugin.getClassLoader());
485 final List<ServletContextListener> listeners = new ArrayList<ServletContextListener>();
486 try
487 {
488 for (final ServletContextListenerModuleDescriptor descriptor : findModuleDescriptorsByType(ServletContextListenerModuleDescriptor.class, plugin))
489 {
490 listeners.add(descriptor.getModule());
491 }
492 }
493 finally
494 {
495 ClassLoaderStack.pop();
496 }
497
498 return new ContextLifecycleManager(context, listeners);
499 }
500
501 private Map<String, String> mergeInitParams(final ServletContext baseContext, final Plugin plugin)
502 {
503 final Map<String, String> mergedInitParams = new HashMap<String, String>();
504 @SuppressWarnings("unchecked")
505 final Enumeration<String> e = baseContext.getInitParameterNames();
506 while (e.hasMoreElements())
507 {
508 final String paramName = e.nextElement();
509 mergedInitParams.put(paramName, baseContext.getInitParameter(paramName));
510 }
511 for (final ServletContextParamModuleDescriptor descriptor : findModuleDescriptorsByType(ServletContextParamModuleDescriptor.class, plugin))
512 {
513 mergedInitParams.put(descriptor.getParamName(), descriptor.getParamValue());
514 }
515 return Collections.unmodifiableMap(mergedInitParams);
516 }
517 }
518
519 static <T extends ModuleDescriptor<?>> Iterable<T> findModuleDescriptorsByType(final Class<T> type, final Plugin plugin)
520 {
521 final Set<T> descriptors = new HashSet<T>();
522 for (final ModuleDescriptor<?> descriptor : plugin.getModuleDescriptors())
523 {
524 if (type.isAssignableFrom(descriptor.getClass()))
525 {
526 descriptors.add(type.cast(descriptor));
527 }
528 }
529 return descriptors;
530 }
531
532 static final class ContextLifecycleManager
533 {
534 private final ServletContext servletContext;
535 private final Iterable<ServletContextListener> listeners;
536
537 ContextLifecycleManager(final ServletContext servletContext, final Iterable<ServletContextListener> listeners)
538 {
539 this.servletContext = servletContext;
540 this.listeners = listeners;
541 for (final ServletContextListener listener : listeners)
542 {
543 listener.contextInitialized(new ServletContextEvent(servletContext));
544 }
545 }
546
547 ServletContext getServletContext()
548 {
549 return servletContext;
550 }
551
552 void contextDestroyed()
553 {
554 final ServletContextEvent event = new ServletContextEvent(servletContext);
555 for (final ServletContextListener listener : listeners)
556 {
557 listener.contextDestroyed(event);
558 }
559 }
560 }
561 }