UrlPipeline.java

  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.util.Enumeration;
  13. import java.util.NoSuchElementException;
  14. import java.util.Set;

  15. import javax.servlet.Filter;
  16. import javax.servlet.FilterChain;
  17. import javax.servlet.ServletConfig;
  18. import javax.servlet.ServletContext;
  19. import javax.servlet.ServletException;
  20. import javax.servlet.ServletRequest;
  21. import javax.servlet.ServletResponse;
  22. import javax.servlet.http.HttpServlet;
  23. import javax.servlet.http.HttpServletRequest;
  24. import javax.servlet.http.HttpServletResponse;

  25. /**
  26.  * Encapsulates the entire serving stack for a single URL.
  27.  * <p>
  28.  * Subclasses provide the implementation of {@link #match(HttpServletRequest)},
  29.  * which is called by {@link MetaServlet} in registration order to determine the
  30.  * pipeline that will be used to handle a request.
  31.  * <p>
  32.  * The very bottom of each pipeline is a single {@link HttpServlet} that will
  33.  * handle producing the response for this pipeline's URL. {@link Filter}s may
  34.  * also be registered and applied around the servlet's processing, to manage
  35.  * request attributes, set standard response headers, or completely override the
  36.  * response generation.
  37.  */
  38. abstract class UrlPipeline {
  39.     /** Filters to apply around {@link #servlet}; may be empty but never null. */
  40.     private final Filter[] filters;

  41.     /** Instance that must generate the response; never null. */
  42.     private final HttpServlet servlet;

  43.     UrlPipeline(Filter[] filters, HttpServlet servlet) {
  44.         this.filters = filters;
  45.         this.servlet = servlet;
  46.     }

  47.     /**
  48.      * Initialize all contained filters and servlets.
  49.      *
  50.      * @param context
  51.      *            the servlet container context our {@link MetaServlet} is
  52.      *            running within.
  53.      * @param inited
  54.      *            <i>(input/output)</i> the set of filters and servlets which
  55.      *            have already been initialized within the container context. If
  56.      *            those same instances appear in this pipeline they are not
  57.      *            initialized a second time. Filters and servlets that are first
  58.      *            initialized by this pipeline will be added to this set.
  59.      * @throws ServletException
  60.      *             a filter or servlet is unable to initialize.
  61.      */
  62.     void init(ServletContext context, Set<Object> inited)
  63.             throws ServletException {
  64.         for (Filter ref : filters)
  65.             initFilter(ref, context, inited);
  66.         initServlet(servlet, context, inited);
  67.     }

  68.     private static void initFilter(final Filter ref,
  69.             final ServletContext context, final Set<Object> inited)
  70.             throws ServletException {
  71.         if (!inited.contains(ref)) {
  72.             ref.init(new NoParameterFilterConfig(ref.getClass().getName(),
  73.                     context));
  74.             inited.add(ref);
  75.         }
  76.     }

  77.     private static void initServlet(final HttpServlet ref,
  78.             final ServletContext context, final Set<Object> inited)
  79.             throws ServletException {
  80.         if (!inited.contains(ref)) {
  81.             ref.init(new ServletConfig() {
  82.                 @Override
  83.                 public String getInitParameter(String name) {
  84.                     return null;
  85.                 }

  86.                 @Override
  87.                 public Enumeration<String> getInitParameterNames() {
  88.                     return new Enumeration<>() {

  89.                         @Override
  90.                         public boolean hasMoreElements() {
  91.                             return false;
  92.                         }

  93.                         @Override
  94.                         public String nextElement() {
  95.                             throw new NoSuchElementException();
  96.                         }
  97.                     };
  98.                 }

  99.                 @Override
  100.                 public ServletContext getServletContext() {
  101.                     return context;
  102.                 }

  103.                 @Override
  104.                 public String getServletName() {
  105.                     return ref.getClass().getName();
  106.                 }
  107.             });
  108.             inited.add(ref);
  109.         }
  110.     }

  111.     /**
  112.      * Destroy all contained filters and servlets.
  113.      *
  114.      * @param destroyed
  115.      *            <i>(input/output)</i> the set of filters and servlets which
  116.      *            have already been destroyed within the container context. If
  117.      *            those same instances appear in this pipeline they are not
  118.      *            destroyed a second time. Filters and servlets that are first
  119.      *            destroyed by this pipeline will be added to this set.
  120.      */
  121.     void destroy(Set<Object> destroyed) {
  122.         for (Filter ref : filters)
  123.             destroyFilter(ref, destroyed);
  124.         destroyServlet(servlet, destroyed);
  125.     }

  126.     private static void destroyFilter(Filter ref, Set<Object> destroyed) {
  127.         if (!destroyed.contains(ref)) {
  128.             ref.destroy();
  129.             destroyed.add(ref);
  130.         }
  131.     }

  132.     private static void destroyServlet(HttpServlet ref, Set<Object> destroyed) {
  133.         if (!destroyed.contains(ref)) {
  134.             ref.destroy();
  135.             destroyed.add(ref);
  136.         }
  137.     }

  138.     /**
  139.      * Determine if this pipeline handles the request's URL.
  140.      * <p>
  141.      * This method should match on the request's {@code getPathInfo()} method,
  142.      * as {@link MetaServlet} passes the request along as-is to each pipeline's
  143.      * match method.
  144.      *
  145.      * @param req
  146.      *            current HTTP request being considered by {@link MetaServlet}.
  147.      * @return {@code true} if this pipeline is configured to handle the
  148.      *         request; {@code false} otherwise.
  149.      */
  150.     abstract boolean match(HttpServletRequest req);

  151.     /**
  152.      * Execute the filters and the servlet on the request.
  153.      * <p>
  154.      * Invoked by {@link MetaServlet} once {@link #match(HttpServletRequest)}
  155.      * has determined this pipeline is the correct pipeline to handle the
  156.      * current request.
  157.      *
  158.      * @param req
  159.      *            current HTTP request.
  160.      * @param rsp
  161.      *            current HTTP response.
  162.      * @throws ServletException
  163.      *             request cannot be completed.
  164.      * @throws IOException
  165.      *             IO error prevents the request from being completed.
  166.      */
  167.     void service(HttpServletRequest req, HttpServletResponse rsp)
  168.             throws ServletException, IOException {
  169.         if (0 < filters.length)
  170.             new Chain(filters, servlet).doFilter(req, rsp);
  171.         else
  172.             servlet.service(req, rsp);
  173.     }

  174.     private static class Chain implements FilterChain {
  175.         private final Filter[] filters;

  176.         private final HttpServlet servlet;

  177.         private int filterIdx;

  178.         Chain(Filter[] filters, HttpServlet servlet) {
  179.             this.filters = filters;
  180.             this.servlet = servlet;
  181.         }

  182.         @Override
  183.         public void doFilter(ServletRequest req, ServletResponse rsp)
  184.                 throws IOException, ServletException {
  185.             if (filterIdx < filters.length)
  186.                 filters[filterIdx++].doFilter(req, rsp, this);
  187.             else
  188.                 servlet.service(req, rsp);
  189.         }
  190.     }
  191. }