diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
new file mode 100644
index 000000000..7b88ae346
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import java.io.File;
+import java.text.MessageFormat;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.http.server.glue.ErrorServlet;
+import org.eclipse.jgit.http.server.glue.MetaFilter;
+import org.eclipse.jgit.http.server.glue.RegexGroupFilter;
+import org.eclipse.jgit.http.server.glue.ServletBinder;
+import org.eclipse.jgit.http.server.resolver.AsIsFileService;
+import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory;
+import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.UploadPack;
+import org.eclipse.jgit.transport.resolver.FileResolver;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.RepositoryResolver;
+import org.eclipse.jgit.transport.resolver.UploadPackFactory;
+import org.eclipse.jgit.util.StringUtils;
+
+/**
+ * Handles Git repository access over HTTP.
+ *
+ * Applications embedding this filter should map a directory path within the
+ * application to this filter. For a servlet version, see {@link GitServlet}.
+ *
+ * Applications may wish to add additional repository action URLs to this
+ * servlet by taking advantage of its extension from {@link MetaFilter}.
+ * Callers may register their own URL suffix translations through
+ * {@link #serve(String)}, or their regex translations through
+ * {@link #serveRegex(String)}. Each translation should contain a complete
+ * filter pipeline which ends with the HttpServlet that should handle the
+ * requested action.
+ */
+public class GitFilter extends MetaFilter {
+ private volatile boolean initialized;
+
+ private RepositoryResolver resolver;
+
+ private AsIsFileService asIs = new AsIsFileService();
+
+ private UploadPackFactory uploadPackFactory = new DefaultUploadPackFactory();
+
+ private ReceivePackFactory receivePackFactory = new DefaultReceivePackFactory();
+
+ private final List uploadPackFilters = new LinkedList();
+
+ private final List receivePackFilters = new LinkedList();
+
+ /**
+ * New servlet that will load its base directory from {@code web.xml}.
+ *
+ * The required parameter {@code base-path} must be configured to point to
+ * the local filesystem directory where all served Git repositories reside.
+ */
+ public GitFilter() {
+ // Initialized above by field declarations.
+ }
+
+ /**
+ * New servlet configured with a specific resolver.
+ *
+ * @param resolver
+ * the resolver to use when matching URL to Git repository. If
+ * null the {@code base-path} parameter will be looked for in the
+ * parameter table during init, which usually comes from the
+ * {@code web.xml} file of the web application.
+ */
+ public void setRepositoryResolver(RepositoryResolver resolver) {
+ assertNotInitialized();
+ this.resolver = resolver;
+ }
+
+ /**
+ * @param f
+ * the filter to validate direct access to repository files
+ * through a dumb client. If {@code null} then dumb client
+ * support is completely disabled.
+ */
+ public void setAsIsFileService(AsIsFileService f) {
+ assertNotInitialized();
+ this.asIs = f != null ? f : AsIsFileService.DISABLED;
+ }
+
+ /**
+ * @param f
+ * the factory to construct and configure an {@link UploadPack}
+ * session when a fetch or clone is requested by a client.
+ */
+ @SuppressWarnings("unchecked")
+ public void setUploadPackFactory(UploadPackFactory f) {
+ assertNotInitialized();
+ this.uploadPackFactory = f != null ? f : (UploadPackFactory)UploadPackFactory.DISABLED;
+ }
+
+ /**
+ * @param filter
+ * filter to apply before any of the UploadPack operations. The
+ * UploadPack instance is available in the request attribute
+ * {@link ServletUtils#ATTRIBUTE_HANDLER}.
+ */
+ public void addUploadPackFilter(Filter filter) {
+ assertNotInitialized();
+ uploadPackFilters.add(filter);
+ }
+
+ /**
+ * @param f
+ * the factory to construct and configure a {@link ReceivePack}
+ * session when a push is requested by a client.
+ */
+ @SuppressWarnings("unchecked")
+ public void setReceivePackFactory(ReceivePackFactory f) {
+ assertNotInitialized();
+ this.receivePackFactory = f != null ? f : (ReceivePackFactory)ReceivePackFactory.DISABLED;
+ }
+
+ /**
+ * @param filter
+ * filter to apply before any of the ReceivePack operations. The
+ * ReceivePack instance is available in the request attribute
+ * {@link ServletUtils#ATTRIBUTE_HANDLER}.
+ */
+ public void addReceivePackFilter(Filter filter) {
+ assertNotInitialized();
+ receivePackFilters.add(filter);
+ }
+
+ private void assertNotInitialized() {
+ if (initialized)
+ throw new IllegalStateException(HttpServerText.get().alreadyInitializedByContainer);
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ super.init(filterConfig);
+
+ if (resolver == null) {
+ File root = getFile(filterConfig, "base-path");
+ boolean exportAll = getBoolean(filterConfig, "export-all");
+ setRepositoryResolver(new FileResolver(root, exportAll));
+ }
+
+ initialized = true;
+
+ if (uploadPackFactory != UploadPackFactory.DISABLED) {
+ ServletBinder b = serve("*/git-upload-pack");
+ b = b.through(new UploadPackServlet.Factory(uploadPackFactory));
+ for (Filter f : uploadPackFilters)
+ b = b.through(f);
+ b.with(new UploadPackServlet());
+ }
+
+ if (receivePackFactory != ReceivePackFactory.DISABLED) {
+ ServletBinder b = serve("*/git-receive-pack");
+ b = b.through(new ReceivePackServlet.Factory(receivePackFactory));
+ for (Filter f : receivePackFilters)
+ b = b.through(f);
+ b.with(new ReceivePackServlet());
+ }
+
+ ServletBinder refs = serve("*/" + Constants.INFO_REFS);
+ if (uploadPackFactory != UploadPackFactory.DISABLED) {
+ refs = refs.through(new UploadPackServlet.InfoRefs(
+ uploadPackFactory, uploadPackFilters));
+ }
+ if (receivePackFactory != ReceivePackFactory.DISABLED) {
+ refs = refs.through(new ReceivePackServlet.InfoRefs(
+ receivePackFactory, receivePackFilters));
+ }
+ if (asIs != AsIsFileService.DISABLED) {
+ refs = refs.through(new IsLocalFilter());
+ refs = refs.through(new AsIsFileFilter(asIs));
+ refs.with(new InfoRefsServlet());
+ } else
+ refs.with(new ErrorServlet(HttpServletResponse.SC_FORBIDDEN));
+
+ if (asIs != AsIsFileService.DISABLED) {
+ final IsLocalFilter mustBeLocal = new IsLocalFilter();
+ final AsIsFileFilter enabled = new AsIsFileFilter(asIs);
+
+ serve("*/" + Constants.HEAD)//
+ .through(mustBeLocal)//
+ .through(enabled)//
+ .with(new TextFileServlet(Constants.HEAD));
+
+ final String info_alternates = "objects/info/alternates";
+ serve("*/" + info_alternates)//
+ .through(mustBeLocal)//
+ .through(enabled)//
+ .with(new TextFileServlet(info_alternates));
+
+ final String http_alternates = "objects/info/http-alternates";
+ serve("*/" + http_alternates)//
+ .through(mustBeLocal)//
+ .through(enabled)//
+ .with(new TextFileServlet(http_alternates));
+
+ serve("*/objects/info/packs")//
+ .through(mustBeLocal)//
+ .through(enabled)//
+ .with(new InfoPacksServlet());
+
+ serveRegex("^/(.*)/objects/([0-9a-f]{2}/[0-9a-f]{38})$")//
+ .through(mustBeLocal)//
+ .through(enabled)//
+ .through(new RegexGroupFilter(2))//
+ .with(new ObjectFileServlet.Loose());
+
+ serveRegex("^/(.*)/objects/(pack/pack-[0-9a-f]{40}\\.pack)$")//
+ .through(mustBeLocal)//
+ .through(enabled)//
+ .through(new RegexGroupFilter(2))//
+ .with(new ObjectFileServlet.Pack());
+
+ serveRegex("^/(.*)/objects/(pack/pack-[0-9a-f]{40}\\.idx)$")//
+ .through(mustBeLocal)//
+ .through(enabled)//
+ .through(new RegexGroupFilter(2))//
+ .with(new ObjectFileServlet.PackIdx());
+ }
+ }
+
+ private static File getFile(FilterConfig cfg, String param)
+ throws ServletException {
+ String n = cfg.getInitParameter(param);
+ if (n == null || "".equals(n))
+ throw new ServletException(MessageFormat.format(HttpServerText.get().parameterNotSet, param));
+
+ File path = new File(n);
+ if (!path.exists())
+ throw new ServletException(MessageFormat.format(HttpServerText.get().pathForParamNotFound, path, param));
+ return path;
+ }
+
+ private static boolean getBoolean(FilterConfig cfg, String param)
+ throws ServletException {
+ String n = cfg.getInitParameter(param);
+ if (n == null)
+ return false;
+ try {
+ return StringUtils.toBoolean(n);
+ } catch (IllegalArgumentException err) {
+ throw new ServletException(MessageFormat.format(HttpServerText.get().invalidBoolean, param, n));
+ }
+ }
+
+ @Override
+ protected ServletBinder register(ServletBinder binder) {
+ if (resolver == null)
+ throw new IllegalStateException(HttpServerText.get().noResolverAvailable);
+ binder = binder.through(new NoCacheFilter());
+ binder = binder.through(new RepositoryFilter(resolver));
+ return binder;
+ }
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java
index caa84e46b..ef3dc2cc7 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java
@@ -43,32 +43,22 @@
package org.eclipse.jgit.http.server;
-import java.io.File;
-import java.text.MessageFormat;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.Enumeration;
import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jgit.http.server.glue.ErrorServlet;
import org.eclipse.jgit.http.server.glue.MetaServlet;
-import org.eclipse.jgit.http.server.glue.RegexGroupFilter;
-import org.eclipse.jgit.http.server.glue.ServletBinder;
import org.eclipse.jgit.http.server.resolver.AsIsFileService;
-import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory;
-import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.UploadPack;
-import org.eclipse.jgit.transport.resolver.FileResolver;
import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
import org.eclipse.jgit.transport.resolver.RepositoryResolver;
import org.eclipse.jgit.transport.resolver.UploadPackFactory;
-import org.eclipse.jgit.util.StringUtils;
/**
* Handles Git repository access over HTTP.
@@ -107,19 +97,7 @@ import org.eclipse.jgit.util.StringUtils;
public class GitServlet extends MetaServlet {
private static final long serialVersionUID = 1L;
- private volatile boolean initialized;
-
- private RepositoryResolver resolver;
-
- private AsIsFileService asIs = new AsIsFileService();
-
- private UploadPackFactory uploadPackFactory = new DefaultUploadPackFactory();
-
- private ReceivePackFactory receivePackFactory = new DefaultReceivePackFactory();
-
- private final List uploadPackFilters = new LinkedList();
-
- private final List receivePackFilters = new LinkedList();
+ private final GitFilter gitFilter;
/**
* New servlet that will load its base directory from {@code web.xml}.
@@ -128,7 +106,8 @@ public class GitServlet extends MetaServlet {
* the local filesystem directory where all served Git repositories reside.
*/
public GitServlet() {
- // Initialized above by field declarations.
+ super(new GitFilter());
+ gitFilter = (GitFilter) getDelegateFilter();
}
/**
@@ -141,8 +120,7 @@ public class GitServlet extends MetaServlet {
* {@code web.xml} file of the web application.
*/
public void setRepositoryResolver(RepositoryResolver resolver) {
- assertNotInitialized();
- this.resolver = resolver;
+ gitFilter.setRepositoryResolver(resolver);
}
/**
@@ -152,8 +130,7 @@ public class GitServlet extends MetaServlet {
* support is completely disabled.
*/
public void setAsIsFileService(AsIsFileService f) {
- assertNotInitialized();
- this.asIs = f != null ? f : AsIsFileService.DISABLED;
+ gitFilter.setAsIsFileService(f);
}
/**
@@ -163,8 +140,7 @@ public class GitServlet extends MetaServlet {
*/
@SuppressWarnings("unchecked")
public void setUploadPackFactory(UploadPackFactory f) {
- assertNotInitialized();
- this.uploadPackFactory = f != null ? f : (UploadPackFactory)UploadPackFactory.DISABLED;
+ gitFilter.setUploadPackFactory(f);
}
/**
@@ -174,8 +150,7 @@ public class GitServlet extends MetaServlet {
* {@link ServletUtils#ATTRIBUTE_HANDLER}.
*/
public void addUploadPackFilter(Filter filter) {
- assertNotInitialized();
- uploadPackFilters.add(filter);
+ gitFilter.addUploadPackFilter(filter);
}
/**
@@ -185,8 +160,7 @@ public class GitServlet extends MetaServlet {
*/
@SuppressWarnings("unchecked")
public void setReceivePackFactory(ReceivePackFactory f) {
- assertNotInitialized();
- this.receivePackFactory = f != null ? f : (ReceivePackFactory)ReceivePackFactory.DISABLED;
+ gitFilter.setReceivePackFactory(f);
}
/**
@@ -196,133 +170,27 @@ public class GitServlet extends MetaServlet {
* {@link ServletUtils#ATTRIBUTE_HANDLER}.
*/
public void addReceivePackFilter(Filter filter) {
- assertNotInitialized();
- receivePackFilters.add(filter);
- }
-
- private void assertNotInitialized() {
- if (initialized)
- throw new IllegalStateException(HttpServerText.get().alreadyInitializedByContainer);
+ gitFilter.addReceivePackFilter(filter);
}
@Override
public void init(final ServletConfig config) throws ServletException {
- super.init(config);
-
- if (resolver == null) {
- final File root = getFile("base-path");
- final boolean exportAll = getBoolean("export-all");
- setRepositoryResolver(new FileResolver(root, exportAll));
- }
-
- initialized = true;
-
- if (uploadPackFactory != UploadPackFactory.DISABLED) {
- ServletBinder b = serve("*/git-upload-pack");
- b = b.through(new UploadPackServlet.Factory(uploadPackFactory));
- for (Filter f : uploadPackFilters)
- b = b.through(f);
- b.with(new UploadPackServlet());
- }
-
- if (receivePackFactory != ReceivePackFactory.DISABLED) {
- ServletBinder b = serve("*/git-receive-pack");
- b = b.through(new ReceivePackServlet.Factory(receivePackFactory));
- for (Filter f : receivePackFilters)
- b = b.through(f);
- b.with(new ReceivePackServlet());
- }
-
- ServletBinder refs = serve("*/" + Constants.INFO_REFS);
- if (uploadPackFactory != UploadPackFactory.DISABLED) {
- refs = refs.through(new UploadPackServlet.InfoRefs(
- uploadPackFactory, uploadPackFilters));
- }
- if (receivePackFactory != ReceivePackFactory.DISABLED) {
- refs = refs.through(new ReceivePackServlet.InfoRefs(
- receivePackFactory, receivePackFilters));
- }
- if (asIs != AsIsFileService.DISABLED) {
- refs = refs.through(new IsLocalFilter());
- refs = refs.through(new AsIsFileFilter(asIs));
- refs.with(new InfoRefsServlet());
- } else
- refs.with(new ErrorServlet(HttpServletResponse.SC_FORBIDDEN));
-
- if (asIs != AsIsFileService.DISABLED) {
- final IsLocalFilter mustBeLocal = new IsLocalFilter();
- final AsIsFileFilter enabled = new AsIsFileFilter(asIs);
-
- serve("*/" + Constants.HEAD)//
- .through(mustBeLocal)//
- .through(enabled)//
- .with(new TextFileServlet(Constants.HEAD));
-
- final String info_alternates = "objects/info/alternates";
- serve("*/" + info_alternates)//
- .through(mustBeLocal)//
- .through(enabled)//
- .with(new TextFileServlet(info_alternates));
-
- final String http_alternates = "objects/info/http-alternates";
- serve("*/" + http_alternates)//
- .through(mustBeLocal)//
- .through(enabled)//
- .with(new TextFileServlet(http_alternates));
-
- serve("*/objects/info/packs")//
- .through(mustBeLocal)//
- .through(enabled)//
- .with(new InfoPacksServlet());
-
- serveRegex("^/(.*)/objects/([0-9a-f]{2}/[0-9a-f]{38})$")//
- .through(mustBeLocal)//
- .through(enabled)//
- .through(new RegexGroupFilter(2))//
- .with(new ObjectFileServlet.Loose());
-
- serveRegex("^/(.*)/objects/(pack/pack-[0-9a-f]{40}\\.pack)$")//
- .through(mustBeLocal)//
- .through(enabled)//
- .through(new RegexGroupFilter(2))//
- .with(new ObjectFileServlet.Pack());
-
- serveRegex("^/(.*)/objects/(pack/pack-[0-9a-f]{40}\\.idx)$")//
- .through(mustBeLocal)//
- .through(enabled)//
- .through(new RegexGroupFilter(2))//
- .with(new ObjectFileServlet.PackIdx());
- }
- }
-
- private File getFile(final String param) throws ServletException {
- String n = getInitParameter(param);
- if (n == null || "".equals(n))
- throw new ServletException(MessageFormat.format(HttpServerText.get().parameterNotSet, param));
-
- File path = new File(n);
- if (!path.exists())
- throw new ServletException(MessageFormat.format(HttpServerText.get().pathForParamNotFound, path, param));
- return path;
- }
-
- private boolean getBoolean(String param) throws ServletException {
- String n = getInitParameter(param);
- if (n == null)
- return false;
- try {
- return StringUtils.toBoolean(n);
- } catch (IllegalArgumentException err) {
- throw new ServletException(MessageFormat.format(HttpServerText.get().invalidBoolean, param, n));
- }
- }
-
- @Override
- protected ServletBinder register(ServletBinder binder) {
- if (resolver == null)
- throw new IllegalStateException(HttpServerText.get().noResolverAvailable);
- binder = binder.through(new NoCacheFilter());
- binder = binder.through(new RepositoryFilter(resolver));
- return binder;
+ gitFilter.init(new FilterConfig() {
+ public String getFilterName() {
+ return gitFilter.getClass().getName();
+ }
+
+ public String getInitParameter(String name) {
+ return config.getInitParameter(name);
+ }
+
+ public Enumeration getInitParameterNames() {
+ return config.getInitParameterNames();
+ }
+
+ public ServletContext getServletContext() {
+ return config.getServletContext();
+ }
+ });
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaFilter.java
new file mode 100644
index 000000000..8cb3bea1f
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaFilter.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.glue;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.http.server.HttpServerText;
+
+/**
+ * Generic container filter to manage routing to different pipelines.
+ *
+ * Callers can create and configure a new processing pipeline by using one of
+ * the {@link #serve(String)} or {@link #serveRegex(String)} methods to allocate
+ * a binder for a particular URL pattern.
+ *
+ * Registered filters and servlets are initialized lazily, usually during the
+ * first request. Once initialized the bindings in this servlet cannot be
+ * modified without destroying the servlet and thereby destroying all registered
+ * filters and servlets.
+ */
+public class MetaFilter implements Filter {
+ static final String REGEX_GROUPS = "org.eclipse.jgit.http.server.glue.MetaServlet.serveRegex";
+
+ private ServletContext servletContext;
+
+ private final List bindings;
+
+ private volatile UrlPipeline[] pipelines;
+
+ /** Empty filter with no bindings. */
+ public MetaFilter() {
+ this.bindings = new ArrayList();
+ }
+
+ /**
+ * Construct a binding for a specific path.
+ *
+ * @param path
+ * pattern to match.
+ * @return binder for the passed path.
+ */
+ public ServletBinder serve(String path) {
+ if (path.startsWith("*"))
+ return register(new SuffixPipeline.Binder(path.substring(1)));
+ throw new IllegalArgumentException(MessageFormat.format(HttpServerText
+ .get().pathNotSupported, path));
+ }
+
+ /**
+ * Construct a binding for a regular expression.
+ *
+ * @param expression
+ * the regular expression to pattern match the URL against.
+ * @return binder for the passed expression.
+ */
+ public ServletBinder serveRegex(String expression) {
+ return register(new RegexPipeline.Binder(expression));
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ servletContext = filterConfig.getServletContext();
+ }
+
+ public void destroy() {
+ if (pipelines != null) {
+ Set