From 5f1c5ded4ef18f45dead86307810e40c0ea8c2af Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 21 Jul 2011 20:21:08 +0200 Subject: [PATCH] write mercurial hook module on first request --- .../api/rest/resources/HgHookCallback.java | 80 +++++++ .../main/java/sonia/scm/web/HgCGIServlet.java | 2 +- .../sonia/scm/web/HgHookScriptFilter.java | 206 ++++++++++++++++++ .../java/sonia/scm/web/HgServletModule.java | 10 +- .../src/main/resources/sonia/scm/hghook.py | 17 ++ 5 files changed, 313 insertions(+), 2 deletions(-) create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgHookCallback.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookScriptFilter.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/hghook.py diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgHookCallback.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgHookCallback.java new file mode 100644 index 0000000000..7b9c1973ea --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgHookCallback.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of SCM-Manager; 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 REGENTS 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. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.api.rest.resources; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Response; + +/** + * + * @author Sebastian Sdorra + */ +@Path("hook/hg/{repository}/{type}") +public class HgHookCallback +{ + + /** the logger for HgHookCallback */ + private static final Logger logger = + LoggerFactory.getLogger(HgHookCallback.class); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * + * @param repositoryName + * @param type + * + * @return + */ + @GET + public Response hookCallback(@PathParam("repository") String repositoryName, + @PathParam("type") String type) + { + logger.warn("retrive hg hook {}", type); + + return Response.ok().build(); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java index db3794c39f..4fd8eb82b2 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java @@ -146,7 +146,7 @@ public class HgCGIServlet extends HttpServlet { throw new ServletException("repository not found"); } - + String name = repository.getName(); File directory = handler.getDirectory(repository); String pythonPath = ""; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookScriptFilter.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookScriptFilter.java new file mode 100644 index 0000000000..1fb19f3994 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookScriptFilter.java @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of SCM-Manager; 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 REGENTS 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. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.web; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.SCMContextProvider; +import sonia.scm.io.RegexResourceProcessor; +import sonia.scm.io.ResourceProcessor; +import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.util.IOUtil; +import sonia.scm.util.Util; +import sonia.scm.web.filter.HttpFilter; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @author Sebastian Sdorra + */ +@Singleton +public class HgHookScriptFilter extends HttpFilter +{ + + /** Field description */ + public static final String SCRIPT_TEMPLATE = "/sonia/scm/hghook.py"; + + /** the logger for HgHookScriptFilter */ + private static final Logger logger = + LoggerFactory.getLogger(HgHookScriptFilter.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param context + * @param handler + */ + @Inject + public HgHookScriptFilter(SCMContextProvider context, + HgRepositoryHandler handler) + { + this.context = context; + this.handler = handler; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param request + * @param response + * @param chain + * + * @throws IOException + * @throws ServletException + */ + @Override + protected void doFilter(HttpServletRequest request, + HttpServletResponse response, FilterChain chain) + throws IOException, ServletException + { + if (!written) + { + synchronized (HgHookScriptFilter.class) + { + if (!written) + { + writeHookScript(request); + written = true; + } + } + } + + chain.doFilter(request, response); + } + + /** + * Method description + * + * + * @param request + * + * @throws IOException + */ + private void writeHookScript(HttpServletRequest request) throws IOException + { + StringBuilder url = new StringBuilder(request.getScheme()); + + url.append("://localhost:").append(request.getServerPort()); + url.append(request.getContextPath()).append("/api/rest/hook/hg/"); + + File cgiDirectory = new File(context.getBaseDirectory(), "cgi-bin"); + + IOUtil.mkdirs(cgiDirectory); + + File hgHookScript = new File(cgiDirectory, "hg-hook.py"); + + if (logger.isDebugEnabled()) + { + logger.debug("write hg hook script to '{}'", hgHookScript); + } + + writeScript(hgHookScript, url.toString()); + } + + /** + * Method description + * + * + * + * @param script + * @param url + * + * @throws IOException + */ + private void writeScript(File script, String url) throws IOException + { + InputStream input = null; + OutputStream output = null; + + try + { + input = HgWebConfigWriter.class.getResourceAsStream(SCRIPT_TEMPLATE); + output = new FileOutputStream(script); + + HgConfig config = handler.getConfig(); + ResourceProcessor rp = new RegexResourceProcessor(); + + rp.addVariable("python", config.getPythonBinary()); + rp.addVariable("path", Util.nonNull(config.getPythonPath())); + rp.addVariable("url", url); + rp.process(input, output); + script.setExecutable(true); + } + finally + { + IOUtil.close(input); + IOUtil.close(output); + } + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private SCMContextProvider context; + + /** Field description */ + private HgRepositoryHandler handler; + + /** Field description */ + private volatile boolean written = false; +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java index 7013f8a8db..6385ada2dc 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java @@ -36,8 +36,8 @@ package sonia.scm.web; //~--- non-JDK imports -------------------------------------------------------- import com.google.inject.servlet.ServletModule; -import sonia.scm.plugin.ext.Extension; +import sonia.scm.plugin.ext.Extension; import sonia.scm.web.filter.BasicAuthenticationFilter; /** @@ -48,6 +48,9 @@ import sonia.scm.web.filter.BasicAuthenticationFilter; public class HgServletModule extends ServletModule { + /** Field description */ + public static final String MAPPING_ALL = "/*"; + /** Field description */ public static final String MAPPING_HG = "/hg/*"; @@ -60,6 +63,11 @@ public class HgServletModule extends ServletModule @Override protected void configureServlets() { + + // write hook script + filter(MAPPING_ALL).through(HgHookScriptFilter.class); + + // register hg cgi servlet filter(MAPPING_HG).through(BasicAuthenticationFilter.class); filter(MAPPING_HG).through(HgPermissionFilter.class); serve(MAPPING_HG).with(HgCGIServlet.class); diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/hghook.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/hghook.py new file mode 100644 index 0000000000..0bcdd50940 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/hghook.py @@ -0,0 +1,17 @@ +#!/usr/bin/env ${python} + +import os, sys, urllib + +pythonPath = "${path}" + +if len(pythonPath) > 0: + pathParts = pythonPath.split(os.pathsep) + for i in range(len(pathParts)): + sys.path.insert(i, pathParts[i]) + +baseUrl = "${url}" + +def scmhook(ui, repo, hooktype, node=None, source=None, **kwargs): + url = baseUrl + os.path.basename(repo.root) + "/" + hooktype + conn = urllib.urlopen(url); + # todo validate (if conn.code == 200:)