From 4a6fcb4aefcb7dbd6481a15f6acf70c07a311133 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 26 Sep 2010 16:17:39 +0200 Subject: [PATCH] serve mercurial repositories with hgweb cgi --- plugins/scm-hg-plugin/pom.xml | 7 + .../api/rest/resources/HgConfigResource.java | 14 +- .../java/sonia/scm/repository/HgConfig.java | 27 +++- .../java/sonia/scm/web/HgServletModule.java | 46 ++++++ .../java/sonia/scm/web/HgWebConfigWriter.java | 150 ++++++++++++++++++ .../main/java/sonia/scm/web/HgWebPlugin.java | 5 +- .../src/main/resources/sonia/scm/hg.config.js | 5 + .../src/main/resources/sonia/scm/hgweb.cgi | 6 + 8 files changed, 253 insertions(+), 7 deletions(-) create mode 100644 plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java create mode 100644 plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebConfigWriter.java create mode 100644 plugins/scm-hg-plugin/src/main/resources/sonia/scm/hgweb.cgi diff --git a/plugins/scm-hg-plugin/pom.xml b/plugins/scm-hg-plugin/pom.xml index e1b081c9a0..a1d4f1c408 100644 --- a/plugins/scm-hg-plugin/pom.xml +++ b/plugins/scm-hg-plugin/pom.xml @@ -17,6 +17,13 @@ + + javax.servlet + servlet-api + ${servlet.version} + provided + + sonia.scm scm-web-api diff --git a/plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java b/plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java index 507147a7ab..9aecc3f5a5 100644 --- a/plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java +++ b/plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java @@ -15,9 +15,14 @@ import com.google.inject.Singleton; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.RepositoryManager; +import sonia.scm.web.HgWebConfigWriter; //~--- JDK imports ------------------------------------------------------------ +import java.io.IOException; + +import javax.servlet.ServletContext; + import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; @@ -79,16 +84,23 @@ public class HgConfigResource * * * @param uriInfo + * @param servletContext * @param config * * @return + * + * @throws IOException */ @POST @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) - public Response setConfig(@Context UriInfo uriInfo, HgConfig config) + public Response setConfig(@Context UriInfo uriInfo, + @Context ServletContext servletContext, + HgConfig config) + throws IOException { handler.setConfig(config); handler.storeConfig(); + new HgWebConfigWriter(config).write(servletContext); return Response.created(uriInfo.getRequestUri()).build(); } diff --git a/plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java b/plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java index b6ac0f81d9..32a7f3edb8 100644 --- a/plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java +++ b/plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java @@ -29,7 +29,6 @@ public class HgConfig extends BasicRepositoryConfig //~--- get methods ---------------------------------------------------------- - /** * Method description * @@ -41,6 +40,17 @@ public class HgConfig extends BasicRepositoryConfig return hgBinary; } + /** + * Method description + * + * + * @return + */ + public String getPythonBinary() + { + return pythonBinary; + } + /** * Method description * @@ -65,6 +75,17 @@ public class HgConfig extends BasicRepositoryConfig this.hgBinary = hgBinary; } + /** + * Method description + * + * + * @param pythonBinary + */ + public void setPythonBinary(String pythonBinary) + { + this.pythonBinary = pythonBinary; + } + /** * Method description * @@ -78,10 +99,12 @@ public class HgConfig extends BasicRepositoryConfig //~--- fields --------------------------------------------------------------- - /** Field description */ private String hgBinary = "hg"; + /** Field description */ + private String pythonBinary = "python"; + /** Field description */ private File repositoryDirectory; } diff --git a/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java b/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java new file mode 100644 index 0000000000..763c3a67ef --- /dev/null +++ b/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java @@ -0,0 +1,46 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.web; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Scopes; +import com.google.inject.servlet.ServletModule; + +import org.apache.catalina.servlets.CGIServlet; + +import sonia.scm.web.filter.BasicAuthenticationFilter; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author Sebastian Sdorra + */ +public class HgServletModule extends ServletModule +{ + + /** + * Method description + * + */ + @Override + protected void configureServlets() + { + filter("/hg/*").through(BasicAuthenticationFilter.class); + + Map initParams = new HashMap(); + + initParams.put("cgiPathPrefix", "WEB-INF/cgi/hgweb.cgi"); + bind(CGIServlet.class).in(Scopes.SINGLETON); + serve("/hg/*").with(CGIServlet.class, initParams); + } +} diff --git a/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebConfigWriter.java b/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebConfigWriter.java new file mode 100644 index 0000000000..f31ec4dd1d --- /dev/null +++ b/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebConfigWriter.java @@ -0,0 +1,150 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.web; + +//~--- non-JDK imports -------------------------------------------------------- + +import sonia.scm.io.INIConfiguration; +import sonia.scm.io.INIConfigurationWriter; +import sonia.scm.io.INISection; +import sonia.scm.io.RegexResourceProcessor; +import sonia.scm.io.ResourceProcessor; +import sonia.scm.repository.HgConfig; +import sonia.scm.util.Util; + +//~--- 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.ServletContext; + +/** + * + * @author Sebastian Sdorra + */ +public class HgWebConfigWriter +{ + + /** Field description */ + public static final String CGI_PATH = "WEB-INF/cgi/hgweb.cgi"; + + /** Field description */ + public static final String CGI_TEMPLATE = "/sonia/scm/hgweb.cgi"; + + /** Field description */ + public static final String CONFIG_NAME = "hgweb.config"; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param config + */ + public HgWebConfigWriter(HgConfig config) + { + this.config = config; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param context + * + * @throws IOException + */ + public void write(ServletContext context) throws IOException + { + File webConfigFile = new File(config.getRepositoryDirectory(), CONFIG_NAME); + + writeWebConfigFile(webConfigFile); + + String path = context.getRealPath(CGI_PATH); + File cgiFile = new File(path); + File cgiDirectory = cgiFile.getParentFile(); + + if (!cgiDirectory.exists() &&!cgiDirectory.mkdirs()) + { + throw new IOException( + "could not create directory: ".concat(cgiDirectory.getPath())); + } + + System.out.println( cgiFile ); + + writeCGIFile(cgiFile, webConfigFile); + } + + /** + * Method description + * + * + * @param cgiFile + * @param webConfigFile + * + * @throws IOException + */ + private void writeCGIFile(File cgiFile, File webConfigFile) throws IOException + { + InputStream input = null; + OutputStream output = null; + + try + { + input = HgWebConfigWriter.class.getResourceAsStream(CGI_TEMPLATE); + output = new FileOutputStream(cgiFile); + + ResourceProcessor rp = new RegexResourceProcessor(); + + rp.addVariable("python", config.getPythonBinary()); + rp.addVariable("config", webConfigFile.getAbsolutePath()); + rp.process(input, output); + } + finally + { + Util.close(input); + Util.close(output); + } + } + + /** + * Method description + * + * + * @param webConfigFile + * + * @throws IOException + */ + private void writeWebConfigFile(File webConfigFile) throws IOException + { + INIConfiguration webConfig = new INIConfiguration(); + INISection pathsSection = new INISection("paths"); + String path = config.getRepositoryDirectory().getAbsolutePath(); + + if (!path.endsWith(File.separator)) + { + path = path.concat(File.separator); + } + + pathsSection.setParameter("/", path.concat("*")); + webConfig.addSection(pathsSection); + new INIConfigurationWriter().write(webConfig, webConfigFile); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private HgConfig config; +} diff --git a/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebPlugin.java b/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebPlugin.java index e2ae03cba4..dea962509a 100644 --- a/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebPlugin.java +++ b/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebPlugin.java @@ -7,10 +7,6 @@ package sonia.scm.web; -//~--- JDK imports ------------------------------------------------------------ - -import java.io.InputStream; - /** * * @author Sebastian Sdorra @@ -46,5 +42,6 @@ public class HgWebPlugin implements ScmWebPlugin public void contextInitialized(ScmWebPluginContext context) { context.addScriptResource(new ClasspathWebResource(SCRIPT)); + context.addInjectModule(new HgServletModule()); } } diff --git a/plugins/scm-hg-plugin/src/main/resources/sonia/scm/hg.config.js b/plugins/scm-hg-plugin/src/main/resources/sonia/scm/hg.config.js index 44861ab69e..c056644642 100644 --- a/plugins/scm-hg-plugin/src/main/resources/sonia/scm/hg.config.js +++ b/plugins/scm-hg-plugin/src/main/resources/sonia/scm/hg.config.js @@ -10,6 +10,11 @@ registerConfigPanel({ fieldLabel : 'HG Binary', name : 'hgBinary', allowBlank : false + },{ + xtype : 'textfield', + fieldLabel : 'Python Binary', + name : 'pythonBinary', + allowBlank : false },{ xtype: 'textfield', name: 'repositoryDirectory', diff --git a/plugins/scm-hg-plugin/src/main/resources/sonia/scm/hgweb.cgi b/plugins/scm-hg-plugin/src/main/resources/sonia/scm/hgweb.cgi new file mode 100644 index 0000000000..3052701157 --- /dev/null +++ b/plugins/scm-hg-plugin/src/main/resources/sonia/scm/hgweb.cgi @@ -0,0 +1,6 @@ +#!/usr/bin/env ${python} +config = "${config}" +from mercurial import demandimport; demandimport.enable() +from mercurial.hgweb import hgweb, wsgicgi +application = hgweb(config) +wsgicgi.launch(application)