PageRenderTime 26ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/plugin/src/main/java/com/atlassian/plugin/remotable/plugin/loader/universalbinary/UBDispatchFilter.java

https://bitbucket.org/rodogu/remotable-plugins
Java | 341 lines | 284 code | 36 blank | 21 comment | 36 complexity | 53410e416034540493e4794ed1da1cdb MD5 | raw file
  1. package com.atlassian.plugin.remotable.plugin.loader.universalbinary;
  2. import com.atlassian.plugin.Plugin;
  3. import com.atlassian.plugin.PluginAccessor;
  4. import com.atlassian.plugin.event.PluginEventListener;
  5. import com.atlassian.plugin.event.PluginEventManager;
  6. import com.atlassian.plugin.event.events.PluginDisabledEvent;
  7. import com.atlassian.plugin.servlet.filter.IteratingFilterChain;
  8. import com.atlassian.plugin.servlet.util.DefaultPathMapper;
  9. import com.atlassian.plugin.servlet.util.PathMapper;
  10. import com.atlassian.sal.api.ApplicationProperties;
  11. import com.atlassian.security.random.SecureRandomFactory;
  12. import com.atlassian.util.concurrent.CopyOnWriteMap;
  13. import com.google.common.base.Function;
  14. import com.google.common.collect.MapMaker;
  15. import org.slf4j.Logger;
  16. import org.slf4j.LoggerFactory;
  17. import org.springframework.beans.factory.DisposableBean;
  18. import org.springframework.beans.factory.annotation.Autowired;
  19. import org.springframework.stereotype.Component;
  20. import javax.servlet.*;
  21. import javax.servlet.http.HttpServlet;
  22. import javax.servlet.http.HttpServletRequest;
  23. import java.io.IOException;
  24. import java.security.SecureRandom;
  25. import java.util.Collections;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.concurrent.ConcurrentHashMap;
  29. import java.util.regex.Matcher;
  30. import java.util.regex.Pattern;
  31. import static com.google.common.collect.Lists.newArrayList;
  32. /**
  33. *
  34. */
  35. @Component("ubDispatchFilter")
  36. public class UBDispatchFilter implements DisposableBean, Filter
  37. {
  38. private final PathMapper servletPathMapper;
  39. private final PathMapper filterPathMapper;
  40. private final Map<String, DispatcherEntry<HttpServlet>> servlets;
  41. private final Map<String, DispatcherEntry<Filter>> filters;
  42. private static final SecureRandom random = SecureRandomFactory.newInstance();
  43. private final PluginAccessor pluginAccessor;
  44. private static final Logger log = LoggerFactory.getLogger(UBDispatchFilter.class);
  45. private volatile Map<String, ServletContext> servletContextsByApp;
  46. private final PluginEventManager pluginEventManager;
  47. private final ApplicationProperties applicationProperties;
  48. private final Pattern APP_KEY_FINDER = Pattern.compile("/app/([^/]*)/?.*");
  49. @Autowired
  50. public UBDispatchFilter(PluginAccessor pluginAccessor,
  51. PluginEventManager pluginEventManager, ApplicationProperties applicationProperties)
  52. {
  53. this.pluginAccessor = pluginAccessor;
  54. this.pluginEventManager = pluginEventManager;
  55. this.applicationProperties = applicationProperties;
  56. this.servlets = CopyOnWriteMap.newHashMap();
  57. this.filters = CopyOnWriteMap.newHashMap();
  58. this.servletPathMapper = new DefaultPathMapper();
  59. this.filterPathMapper = new DefaultPathMapper();
  60. pluginEventManager.register(this);
  61. }
  62. public String getLocalMountBaseUrl(String appKey)
  63. {
  64. return applicationProperties.getBaseUrl() + getLocalMountBasePath(appKey);
  65. }
  66. public static String getLocalMountBasePath(String appKey)
  67. {
  68. return "/app/" + appKey;
  69. }
  70. public void mountServlet(String appKey, HttpServlet httpServlet, String... urlPatterns)
  71. {
  72. final Plugin plugin = pluginAccessor.getPlugin(appKey);
  73. ClassLoader cl = plugin.getClassLoader();
  74. DispatcherEntry servletEntry = new DispatcherEntry();
  75. servletEntry.appKey = appKey;
  76. servletEntry.dispatcher = new DelegatingUBServlet(httpServlet, cl, urlPatterns[0]);
  77. servletEntry.paths = urlPatterns;
  78. servlets.put(servletEntry.key, servletEntry);
  79. for (String urlPattern : urlPatterns)
  80. {
  81. servletPathMapper.put(servletEntry.key, getLocalMountBasePath(appKey) + urlPattern);
  82. }
  83. }
  84. public void mountFilter(String appKey, Filter filter, String[] urlPatterns)
  85. {
  86. final Plugin plugin = pluginAccessor.getPlugin(appKey);
  87. ClassLoader cl = plugin.getClassLoader();
  88. DispatcherEntry<Filter> entry = new DispatcherEntry<Filter>();
  89. entry.appKey = appKey;
  90. entry.dispatcher = new DelegatingUBFilter(filter, cl);
  91. entry.paths = urlPatterns;
  92. filters.put(entry.key, entry);
  93. for (String urlPattern : urlPatterns)
  94. {
  95. filterPathMapper.put(entry.key, getLocalMountBasePath(appKey) + urlPattern);
  96. if (urlPattern.equals("/") || urlPattern.equals("/*"))
  97. {
  98. filterPathMapper.put(entry.key, getLocalMountBasePath(appKey));
  99. }
  100. }
  101. }
  102. public void mountResources(String appKey, String resourcePrefix, String urlPattern)
  103. {
  104. final Plugin plugin = pluginAccessor.getPlugin(appKey);
  105. ClassLoader cl = plugin.getClassLoader();
  106. DispatcherEntry<HttpServlet> entry = new DispatcherEntry<HttpServlet>();
  107. entry.dispatcher = new DelegatingUBServlet(
  108. new StaticResourceServlet(plugin, resourcePrefix),
  109. cl,
  110. resourcePrefix
  111. );
  112. entry.appKey = appKey;
  113. servletPathMapper.put(appKey, getLocalMountBasePath(appKey) + urlPattern);
  114. servlets.put(appKey, entry);
  115. }
  116. @PluginEventListener
  117. public void unregister(PluginDisabledEvent event)
  118. {
  119. String appKey = event.getPlugin().getKey();
  120. for (DispatcherEntry<HttpServlet> entry : servlets.values())
  121. {
  122. if (appKey.equals(entry.appKey))
  123. {
  124. servlets.remove(entry.key);
  125. servletPathMapper.put(entry.key, null);
  126. entry.dispatcher.destroy();
  127. }
  128. }
  129. for (DispatcherEntry<Filter> entry : filters.values())
  130. {
  131. if (appKey.equals(entry.appKey))
  132. {
  133. filters.remove(entry.key);
  134. filterPathMapper.put(entry.key, null);
  135. entry.dispatcher.destroy();
  136. }
  137. }
  138. }
  139. @Override
  140. public void init(final FilterConfig filterConfig) throws ServletException
  141. {
  142. this.servletContextsByApp = new MapMaker().makeComputingMap(new Function<String, ServletContext>()
  143. {
  144. @Override
  145. public ServletContext apply(String appKey)
  146. {
  147. return new UBServletContextWrapper(
  148. getLocalMountBasePath(appKey),
  149. pluginAccessor.getPlugin(appKey),
  150. filterConfig.getServletContext(),
  151. new ConcurrentHashMap<String,Object>(),
  152. Collections.<String,String>emptyMap()
  153. );
  154. }
  155. });
  156. }
  157. @Override
  158. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain
  159. ) throws IOException, ServletException
  160. {
  161. HttpServletRequest req = (HttpServletRequest) request;
  162. final String uri = getUri(req);
  163. String key = servletPathMapper.get(uri);
  164. if (key == null)
  165. {
  166. Matcher m = APP_KEY_FINDER.matcher(uri);
  167. if (m.matches())
  168. {
  169. key = m.group(1);
  170. }
  171. }
  172. final String servletKey = key;
  173. List<Filter> filterList = newArrayList();
  174. for (String filterKey : filterPathMapper.getAll(uri))
  175. {
  176. filterList.add(getFilter(filterKey));
  177. }
  178. filterList.add(new Filter()
  179. {
  180. public void init(FilterConfig filterConfig) throws ServletException{}
  181. public void destroy(){}
  182. @Override
  183. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain
  184. ) throws IOException, ServletException
  185. {
  186. if (servletKey != null)
  187. {
  188. final HttpServlet servlet = getServlet(servletKey);
  189. servlet.service(request, response);
  190. }
  191. else
  192. {
  193. chain.doFilter(request, response);
  194. }
  195. }
  196. });
  197. IteratingFilterChain localFilterChain = new IteratingFilterChain(filterList.iterator(), chain);
  198. localFilterChain.doFilter(request, response);
  199. }
  200. private Filter getFilter(String filterKey) throws ServletException
  201. {
  202. DispatcherEntry<Filter> entry = filters.get(filterKey);
  203. if (entry == null)
  204. {
  205. // stale path mapper entry
  206. filterPathMapper.put(filterKey, null);
  207. return null;
  208. }
  209. if (!entry.initialized)
  210. {
  211. FilterConfig filterConfig = new UBFilterConfig(filterKey, Collections.<String, String>emptyMap(),
  212. servletContextsByApp.get(entry.appKey));
  213. entry.dispatcher.init(filterConfig);
  214. entry.initialized = true;
  215. }
  216. return entry.dispatcher;
  217. }
  218. private HttpServlet getServlet(String servletKey) throws ServletException
  219. {
  220. DispatcherEntry<HttpServlet> entry = servlets.get(servletKey);
  221. if (entry == null)
  222. {
  223. // stale path mapper entry
  224. servletPathMapper.put(servletKey, null);
  225. throw new IllegalStateException("Servlet not found but expected: " + servletKey);
  226. }
  227. if (!entry.initialized)
  228. {
  229. ServletConfig servletConfig = new UBServletConfig(servletKey, Collections.<String, String>emptyMap(),
  230. servletContextsByApp.get(entry.appKey));
  231. entry.dispatcher.init(servletConfig);
  232. entry.initialized = true;
  233. }
  234. return entry.dispatcher;
  235. }
  236. @Override
  237. public void destroy()
  238. {
  239. pluginEventManager.unregister(this);
  240. }
  241. /**
  242. * Gets the uri from the request. Copied from Struts 2.1.0.
  243. *
  244. * @param request The request
  245. * @return The uri
  246. */
  247. private static String getUri(HttpServletRequest request)
  248. {
  249. // handle http dispatcher includes.
  250. String uri = (String) request
  251. .getAttribute("javax.servlet.include.servlet_path");
  252. if (uri != null)
  253. {
  254. return uri;
  255. }
  256. uri = getServletPath(request);
  257. if (uri != null && !"".equals(uri))
  258. {
  259. return uri;
  260. }
  261. uri = request.getRequestURI();
  262. return uri.substring(request.getContextPath().length());
  263. }
  264. /**
  265. * Retrieves the current request servlet path.
  266. * Deals with differences between servlet specs (2.2 vs 2.3+).
  267. * Copied from Struts 2.1.0.
  268. *
  269. * @param request the request
  270. * @return the servlet path
  271. */
  272. private static String getServletPath(HttpServletRequest request)
  273. {
  274. String servletPath = request.getServletPath();
  275. String requestUri = request.getRequestURI();
  276. // Detecting other characters that the servlet container cut off (like anything after ';')
  277. if (requestUri != null && servletPath != null && !requestUri.endsWith(servletPath))
  278. {
  279. int pos = requestUri.indexOf(servletPath);
  280. if (pos > -1)
  281. {
  282. servletPath = requestUri.substring(requestUri.indexOf(servletPath));
  283. }
  284. }
  285. if (null != servletPath && !"".equals(servletPath))
  286. {
  287. return servletPath;
  288. }
  289. int startIndex = request.getContextPath().equals("") ? 0 : request.getContextPath().length();
  290. int endIndex = request.getPathInfo() == null ? requestUri.length() : requestUri.lastIndexOf(request.getPathInfo());
  291. if (startIndex > endIndex)
  292. { // this should not happen
  293. endIndex = startIndex;
  294. }
  295. return requestUri.substring(startIndex, endIndex);
  296. }
  297. private static class DispatcherEntry<M>
  298. {
  299. public final String key = String.valueOf(random.nextLong());
  300. public String appKey;
  301. public M dispatcher;
  302. public String[] paths;
  303. public volatile boolean initialized = false;
  304. }
  305. }