You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MetaFilter.java 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. * Copyright (C) 2009-2010, Google Inc. and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.http.server.glue;
  11. import java.io.IOException;
  12. import java.text.MessageFormat;
  13. import java.util.AbstractSet;
  14. import java.util.ArrayList;
  15. import java.util.IdentityHashMap;
  16. import java.util.Iterator;
  17. import java.util.List;
  18. import java.util.Map;
  19. import java.util.Set;
  20. import java.util.regex.Pattern;
  21. import javax.servlet.Filter;
  22. import javax.servlet.FilterChain;
  23. import javax.servlet.FilterConfig;
  24. import javax.servlet.ServletContext;
  25. import javax.servlet.ServletException;
  26. import javax.servlet.ServletRequest;
  27. import javax.servlet.ServletResponse;
  28. import javax.servlet.http.HttpServletRequest;
  29. import javax.servlet.http.HttpServletResponse;
  30. import org.eclipse.jgit.http.server.HttpServerText;
  31. /**
  32. * Generic container filter to manage routing to different pipelines.
  33. * <p>
  34. * Callers can create and configure a new processing pipeline by using one of
  35. * the {@link #serve(String)} or {@link #serveRegex(String)} methods to allocate
  36. * a binder for a particular URL pattern.
  37. * <p>
  38. * Registered filters and servlets are initialized lazily, usually during the
  39. * first request. Once initialized the bindings in this servlet cannot be
  40. * modified without destroying the servlet and thereby destroying all registered
  41. * filters and servlets.
  42. */
  43. public class MetaFilter implements Filter {
  44. static final String REGEX_GROUPS = "org.eclipse.jgit.http.server.glue.MetaServlet.serveRegex";
  45. private ServletContext servletContext;
  46. private final List<ServletBinderImpl> bindings;
  47. private volatile UrlPipeline[] pipelines;
  48. /**
  49. * Empty filter with no bindings.
  50. */
  51. public MetaFilter() {
  52. this.bindings = new ArrayList<>();
  53. }
  54. /**
  55. * Construct a binding for a specific path.
  56. *
  57. * @param path
  58. * pattern to match.
  59. * @return binder for the passed path.
  60. */
  61. public ServletBinder serve(String path) {
  62. if (path.startsWith("*"))
  63. return register(new SuffixPipeline.Binder(path.substring(1)));
  64. throw new IllegalArgumentException(MessageFormat.format(HttpServerText
  65. .get().pathNotSupported, path));
  66. }
  67. /**
  68. * Construct a binding for a regular expression.
  69. *
  70. * @param expression
  71. * the regular expression to pattern match the URL against.
  72. * @return binder for the passed expression.
  73. */
  74. public ServletBinder serveRegex(String expression) {
  75. return register(new RegexPipeline.Binder(expression));
  76. }
  77. /**
  78. * Construct a binding for a regular expression.
  79. *
  80. * @param pattern
  81. * the regular expression to pattern match the URL against.
  82. * @return binder for the passed expression.
  83. */
  84. public ServletBinder serveRegex(Pattern pattern) {
  85. return register(new RegexPipeline.Binder(pattern));
  86. }
  87. /** {@inheritDoc} */
  88. @Override
  89. public void init(FilterConfig filterConfig) throws ServletException {
  90. servletContext = filterConfig.getServletContext();
  91. }
  92. /** {@inheritDoc} */
  93. @Override
  94. public void destroy() {
  95. if (pipelines != null) {
  96. Set<Object> destroyed = newIdentitySet();
  97. for (UrlPipeline p : pipelines)
  98. p.destroy(destroyed);
  99. pipelines = null;
  100. }
  101. }
  102. private static Set<Object> newIdentitySet() {
  103. final Map<Object, Object> m = new IdentityHashMap<>();
  104. return new AbstractSet<Object>() {
  105. @Override
  106. public boolean add(Object o) {
  107. return m.put(o, o) == null;
  108. }
  109. @Override
  110. public boolean contains(Object o) {
  111. return m.keySet().contains(o);
  112. }
  113. @Override
  114. public Iterator<Object> iterator() {
  115. return m.keySet().iterator();
  116. }
  117. @Override
  118. public int size() {
  119. return m.size();
  120. }
  121. };
  122. }
  123. /** {@inheritDoc} */
  124. @Override
  125. public void doFilter(ServletRequest request, ServletResponse response,
  126. FilterChain chain) throws IOException, ServletException {
  127. HttpServletRequest req = (HttpServletRequest) request;
  128. HttpServletResponse res = (HttpServletResponse) response;
  129. UrlPipeline p = find(req);
  130. if (p != null)
  131. p.service(req, res);
  132. else
  133. chain.doFilter(req, res);
  134. }
  135. private UrlPipeline find(HttpServletRequest req) throws ServletException {
  136. for (UrlPipeline p : getPipelines())
  137. if (p.match(req))
  138. return p;
  139. return null;
  140. }
  141. private ServletBinder register(ServletBinderImpl b) {
  142. synchronized (bindings) {
  143. if (pipelines != null)
  144. throw new IllegalStateException(
  145. HttpServerText.get().servletAlreadyInitialized);
  146. bindings.add(b);
  147. }
  148. return register((ServletBinder) b);
  149. }
  150. /**
  151. * Configure a newly created binder.
  152. *
  153. * @param b
  154. * the newly created binder.
  155. * @return binder for the caller, potentially after adding one or more
  156. * filters into the pipeline.
  157. */
  158. protected ServletBinder register(ServletBinder b) {
  159. return b;
  160. }
  161. private UrlPipeline[] getPipelines() throws ServletException {
  162. UrlPipeline[] r = pipelines;
  163. if (r == null) {
  164. synchronized (bindings) {
  165. r = pipelines;
  166. if (r == null) {
  167. r = createPipelines();
  168. pipelines = r;
  169. }
  170. }
  171. }
  172. return r;
  173. }
  174. private UrlPipeline[] createPipelines() throws ServletException {
  175. UrlPipeline[] array = new UrlPipeline[bindings.size()];
  176. for (int i = 0; i < bindings.size(); i++)
  177. array[i] = bindings.get(i).create();
  178. Set<Object> inited = newIdentitySet();
  179. for (UrlPipeline p : array)
  180. p.init(servletContext, inited);
  181. return array;
  182. }
  183. }