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 private final PathMapper servletMapper = new DefaultPathMapper();
51 private final Map<String, ServletModuleDescriptor> servletDescriptors = new HashMap<String, ServletModuleDescriptor>();
52 private final ConcurrentMap<String, LazyLoadedReference<HttpServlet>> servletRefs = new ConcurrentHashMap<String, LazyLoadedReference<HttpServlet>>();
53
54 private final PathMapper filterMapper = new DefaultPathMapper();
55 private final Map<String, ServletFilterModuleDescriptor> filterDescriptors = new HashMap<String, ServletFilterModuleDescriptor>();
56 private final ConcurrentMap<String, LazyLoadedReference<Filter>> filterRefs = new ConcurrentHashMap<String, LazyLoadedReference<Filter>>();
57
58 private final ConcurrentMap<Plugin, LazyLoadedReference<ServletContext>> pluginContextRefs = new ConcurrentHashMap<Plugin, LazyLoadedReference<ServletContext>>();
59
60 public DefaultServletModuleManager(PluginEventManager pluginEventManager)
61 {
62 pluginEventManager.register(this);
63 }
64
65 public void addServletModule(ServletModuleDescriptor descriptor)
66 {
67 servletDescriptors.put(descriptor.getCompleteKey(), descriptor);
68
69 @SuppressWarnings("unchecked")
70 final List<String> paths = descriptor.getPaths();
71 for (String path : paths)
72 {
73 servletMapper.put(descriptor.getCompleteKey(), path);
74 }
75 LazyLoadedReference<HttpServlet> servletRef = servletRefs.remove(descriptor.getCompleteKey());
76 if (servletRef != null)
77 {
78 servletRef.get().destroy();
79 }
80 }
81
82 public HttpServlet getServlet(String path, final ServletConfig servletConfig) throws ServletException
83 {
84 String completeKey = servletMapper.get(path);
85
86 if (completeKey == null)
87 {
88 return null;
89 }
90 ServletModuleDescriptor descriptor = servletDescriptors.get(completeKey);
91 if (descriptor == null)
92 {
93 return null;
94 }
95
96 return getServlet(descriptor, servletConfig);
97 }
98
99 public void removeServletModule(ServletModuleDescriptor descriptor)
100 {
101 servletDescriptors.remove(descriptor.getCompleteKey());
102 servletMapper.put(descriptor.getCompleteKey(), null);
103
104 LazyLoadedReference<HttpServlet> servletRef = servletRefs.remove(descriptor.getCompleteKey());
105 if (servletRef != null)
106 {
107 servletRef.get().destroy();
108 }
109 }
110
111 public void addFilterModule(ServletFilterModuleDescriptor descriptor)
112 {
113 filterDescriptors.put(descriptor.getCompleteKey(), descriptor);
114
115 for (String path : descriptor.getPaths())
116 {
117 filterMapper.put(descriptor.getCompleteKey(), path);
118 }
119 LazyLoadedReference<Filter> filterRef = filterRefs.remove(descriptor.getCompleteKey());
120 if (filterRef != null)
121 {
122 filterRef.get().destroy();
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 HttpServlet servlet = servletRef.get();
226 return servlet;
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240 private Filter getFilter(final ServletFilterModuleDescriptor descriptor, final FilterConfig filterConfig)
241 {
242
243
244 LazyLoadedReference<Filter> filterRef = filterRefs.get(descriptor.getCompleteKey());
245 if (filterRef == null)
246 {
247
248 ServletContext servletContext = getWrappedContext(descriptor.getPlugin(), filterConfig.getServletContext());
249 filterRef = new LazyLoadedFilterReference(descriptor, servletContext);
250
251
252
253 if (filterRefs.putIfAbsent(descriptor.getCompleteKey(), filterRef) != null)
254 {
255 filterRef = filterRefs.get(descriptor.getCompleteKey());
256 }
257 }
258 return filterRef.get();
259 }
260
261
262
263
264
265
266
267
268
269
270
271
272
273 private ServletContext getWrappedContext(Plugin plugin, ServletContext baseContext)
274 {
275 LazyLoadedReference<ServletContext> pluginContextRef = pluginContextRefs.get(plugin);
276 if (pluginContextRef == null)
277 {
278 pluginContextRef = new LazyLoadedContextReference(plugin, baseContext);
279 if (pluginContextRefs.putIfAbsent(plugin, pluginContextRef) != null)
280 {
281 pluginContextRef = pluginContextRefs.get(plugin);
282 }
283 }
284 return pluginContextRef.get();
285 }
286
287 private static final class LazyLoadedFilterReference extends LazyLoadedReference<Filter>
288 {
289 private final ServletFilterModuleDescriptor descriptor;
290 private final ServletContext servletContext;
291
292 private LazyLoadedFilterReference(ServletFilterModuleDescriptor descriptor, ServletContext servletContext)
293 {
294 this.descriptor = descriptor;
295 this.servletContext = servletContext;
296 }
297
298 @Override
299 protected Filter create() throws Exception
300 {
301 Filter filter = new DelegatingPluginFilter(descriptor);
302 filter.init(new PluginFilterConfig(descriptor, servletContext));
303 return filter;
304 }
305 }
306
307 private static final class LazyLoadedServletReference extends LazyLoadedReference<HttpServlet>
308 {
309 private final ServletModuleDescriptor descriptor;
310 private final ServletContext servletContext;
311
312 private LazyLoadedServletReference(ServletModuleDescriptor descriptor, ServletContext servletContext)
313 {
314 this.descriptor = descriptor;
315 this.servletContext = servletContext;
316 }
317
318 @Override
319 protected HttpServlet create() throws Exception
320 {
321 HttpServlet servlet = new DelegatingPluginServlet(descriptor);
322 servlet.init(new PluginServletConfig(descriptor, servletContext));
323 return servlet;
324 }
325 }
326
327 private static final class LazyLoadedContextReference extends LazyLoadedReference<ServletContext>
328 {
329 private final Plugin plugin;
330 private final ServletContext baseContext;
331
332 private LazyLoadedContextReference(Plugin plugin, ServletContext baseContext)
333 {
334 this.plugin = plugin;
335 this.baseContext = baseContext;
336 }
337
338 @Override
339 protected ServletContext create() throws Exception
340 {
341 ConcurrentMap<String, Object> contextAttributes = new ConcurrentHashMap<String, Object>();
342 Map<String, String> initParams = mergeInitParams(baseContext, plugin);
343 ServletContext context = new PluginServletContextWrapper(plugin, baseContext, contextAttributes, initParams);
344
345 ClassLoaderStack.push(plugin.getClassLoader());
346 try
347 {
348 for (ServletContextListenerModuleDescriptor descriptor : findModuleDescriptorsByType(ServletContextListenerModuleDescriptor.class, plugin))
349 {
350 descriptor.getModule().contextInitialized(new ServletContextEvent(context));
351 }
352 }
353 finally
354 {
355 ClassLoaderStack.pop();
356 }
357
358 return context;
359 }
360
361 private Map<String, String> mergeInitParams(ServletContext baseContext, Plugin plugin)
362 {
363 Map<String, String> mergedInitParams = new HashMap<String, String>();
364 for (Enumeration<String> e = baseContext.getInitParameterNames(); e.hasMoreElements(); )
365 {
366 String paramName = e.nextElement();
367 mergedInitParams.put(paramName, baseContext.getInitParameter(paramName));
368 }
369 for (ServletContextParamModuleDescriptor descriptor : findModuleDescriptorsByType(ServletContextParamModuleDescriptor.class, plugin))
370 {
371 mergedInitParams.put(descriptor.getParamName(), descriptor.getParamValue());
372 }
373 return Collections.unmodifiableMap(mergedInitParams);
374 }
375 }
376
377 static <T extends ModuleDescriptor<?>> Iterable<T> findModuleDescriptorsByType(Class<T> type, Plugin plugin)
378 {
379 Set<T> descriptors = new HashSet<T>();
380 for (ModuleDescriptor<?> descriptor : plugin.getModuleDescriptors())
381 {
382 if (type.isAssignableFrom(descriptor.getClass()))
383 {
384 descriptors.add((T) descriptor);
385 }
386 }
387 return descriptors;
388 }
389 }