diff --git a/scm-core/src/main/java/sonia/scm/repository/ChangesetViewerUtil.java b/scm-core/src/main/java/sonia/scm/repository/ChangesetViewerUtil.java index 79da36e354..ec73339a22 100644 --- a/scm-core/src/main/java/sonia/scm/repository/ChangesetViewerUtil.java +++ b/scm-core/src/main/java/sonia/scm/repository/ChangesetViewerUtil.java @@ -37,7 +37,6 @@ package sonia.scm.repository; import com.google.inject.Inject; -import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +48,8 @@ import sonia.scm.util.Util; //~--- JDK imports ------------------------------------------------------------ +import java.io.IOException; + import java.util.Set; /** @@ -103,6 +104,8 @@ public class ChangesetViewerUtil extends CacheClearHook * * @return * + * + * @throws IOException * @throws NotSupportedFeatuerException * @throws RepositoryException */ @@ -133,6 +136,8 @@ public class ChangesetViewerUtil extends CacheClearHook * * @return * + * + * @throws IOException * @throws NotSupportedFeatuerException * @throws RepositoryException */ diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgChangesetViewer.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgChangesetViewer.java index 587fd19594..285efa5565 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgChangesetViewer.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgChangesetViewer.java @@ -38,25 +38,18 @@ package sonia.scm.repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import sonia.scm.io.Command; -import sonia.scm.io.CommandResult; -import sonia.scm.io.SimpleCommand; -import sonia.scm.util.IOUtil; +import sonia.scm.util.AssertUtil; +import sonia.scm.web.HgUtil; //~--- JDK imports ------------------------------------------------------------ +import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; import java.util.HashMap; -import java.util.List; import java.util.Map; -import javax.xml.parsers.ParserConfigurationException; +import javax.xml.bind.JAXBContext; /** * @@ -65,30 +58,17 @@ import javax.xml.parsers.ParserConfigurationException; public class HgChangesetViewer implements ChangesetViewer { - /** Field description */ - public static final String ID_TIP = "tip"; - - /** Field description */ - //J- - public static final String TEMPLATE_CHANGESETS = - "\"" - + "{rev}:{node|short}" - + "{author|escape}" - + "{desc|escape}" - + "{date|isodatesec}" - + "{tags}" - + "{branches}" - + "{file_adds}" - + "{file_mods}" - + "{file_dels}" - + "\""; - //J+ - /** Field description */ public static final String ENV_PENDING = "HG_PENDING"; /** Field description */ - public static final String TEMPLATE_TOTAL = "{rev}"; + public static final String ENV_REVISION_LIMIT = "SCM_REVISION_LIMIT"; + + /** Field description */ + public static final String ENV_REVISION_START = "SCM_REVISION_START"; + + /** Field description */ + public static final String RESOURCE_LOG = "/sonia/scm/hglog.py"; /** the logger for HgChangesetViewer */ private static final Logger logger = @@ -102,11 +82,16 @@ public class HgChangesetViewer implements ChangesetViewer * * * @param handler - * @param repository + * @param repositoryDirectory + * @param changesetPagingResultContext */ - public HgChangesetViewer(HgRepositoryHandler handler, Repository repository) + public HgChangesetViewer(HgRepositoryHandler handler, + File repositoryDirectory, + JAXBContext changesetPagingResultContext) { - this(handler, handler.getDirectory(repository).getAbsolutePath()); + this.handler = handler; + this.repositoryDirectory = repositoryDirectory; + this.changesetPagingResultContext = changesetPagingResultContext; } /** @@ -114,12 +99,14 @@ public class HgChangesetViewer implements ChangesetViewer * * * @param handler - * @param repositoryPath + * @param repository + * @param changesetPagingResultContext */ - public HgChangesetViewer(HgRepositoryHandler handler, String repositoryPath) + public HgChangesetViewer(HgRepositoryHandler handler, Repository repository, + JAXBContext changesetPagingResultContext) { - this.handler = handler; - this.repositoryPath = repositoryPath; + this(handler, handler.getDirectory(repository), + changesetPagingResultContext); } //~--- get methods ---------------------------------------------------------- @@ -131,51 +118,14 @@ public class HgChangesetViewer implements ChangesetViewer * @param max * * @return + * + * @throws IOException */ @Override public ChangesetPagingResult getChangesets(int start, int max) + throws IOException { - ChangesetPagingResult changesets = null; - InputStream in = null; - - try - { - int total = getTotalChangesets(repositoryPath); - int startRev = total - start; - int endRev = total - start - (max - 1); - - if (endRev < 0) - { - endRev = 0; - } - - List changesetList = getChangesets(Integer.toString(startRev), - Integer.toString(endRev)); - - if (changesetList != null) - { - if (total == -1) - { - total = 0; - } - else - { - total++; - } - - changesets = new ChangesetPagingResult(total, changesetList); - } - } - catch (IOException ex) - { - logger.error("could not load changesets", ex); - } - finally - { - IOUtil.close(in); - } - - return changesets; + return getChangesets(String.valueOf(start), String.valueOf(max), false); } /** @@ -183,66 +133,41 @@ public class HgChangesetViewer implements ChangesetViewer * * * @param startRev - * @param endRev + * @param limit * @param pending * * @return * * @throws IOException */ - public List getChangesets(String startRev, String endRev, + public ChangesetPagingResult getChangesets(String startRev, String limit, boolean pending) throws IOException { - List changesetList = null; - InputStream in = null; + AssertUtil.assertIsNotEmpty(startRev); + AssertUtil.assertIsNotEmpty(limit); - try + if (logger.isDebugEnabled()) { - SimpleCommand command = - new SimpleCommand(handler.getConfig().getHgBinary(), "-R", - repositoryPath, "log", "-r", startRev + ":" + endRev, - "--template", TEMPLATE_CHANGESETS); - - if (pending) - { - Map env = new HashMap(); - - env.put(ENV_PENDING, repositoryPath); - command.setEnvironment(env); - } - - CommandResult result = command.execute(); - - if (result.isSuccessfull()) - { - StringBuilder sb = new StringBuilder(""); - - sb.append(result.getOutput()).append(""); - changesetList = new HgChangesetParser().parse( - new InputSource(new StringReader(sb.toString()))); - } - else if (logger.isErrorEnabled()) - { - logger.error( - "command for fetching changesets failed with exit code {} and output {}", - result.getReturnCode(), result.getOutput()); - } - } - catch (ParserConfigurationException ex) - { - logger.error("could not parse changesets", ex); - } - catch (SAXException ex) - { - logger.error("could not unmarshall changesets", ex); - } - finally - { - IOUtil.close(in); + logger.debug("get changesets for repository {}, start: {}, limit: {}", + new Object[] { repositoryDirectory.getName(), + startRev, limit }); } - return changesetList; + Map env = new HashMap(); + + if (pending) + { + env.put(ENV_PENDING, repositoryDirectory.getAbsolutePath()); + } + + env.put(ENV_REVISION_START, startRev); + env.put(ENV_REVISION_LIMIT, limit); + + return HgUtil.getResultFromScript(ChangesetPagingResult.class, + changesetPagingResultContext, + RESOURCE_LOG, handler, + repositoryDirectory, env); } /** @@ -250,55 +175,26 @@ public class HgChangesetViewer implements ChangesetViewer * * * @param startRev - * @param endRev + * @param limit * * @return * * @throws IOException */ - public List getChangesets(String startRev, String endRev) + public ChangesetPagingResult getChangesets(String startRev, String limit) throws IOException { - return getChangesets(startRev, endRev, false); - } - - /** - * Method description - * - * - * @param repositoryPath - * - * @return - * - * @throws IOException - */ - private int getTotalChangesets(String repositoryPath) throws IOException - { - int total = -1; - Command command = new SimpleCommand(handler.getConfig().getHgBinary(), - "-R", repositoryPath, "tip", "--template", - TEMPLATE_TOTAL); - CommandResult result = command.execute(); - - if (result.isSuccessfull()) - { - total = Integer.parseInt(result.getOutput().trim()); - } - else if (logger.isErrorEnabled()) - { - logger.error( - "could not read tip revision, command returned with exit code {} and content {}", - result.getReturnCode(), result.getOutput()); - } - - return total; + return getChangesets(startRev, limit, false); } //~--- fields --------------------------------------------------------------- + /** Field description */ + private JAXBContext changesetPagingResultContext; + /** Field description */ private HgRepositoryHandler handler; /** Field description */ - private String repositoryPath; + private File repositoryDirectory; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryBrowser.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryBrowser.java index 3cda3f9952..467db78431 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryBrowser.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryBrowser.java @@ -50,7 +50,6 @@ import java.io.InputStream; import java.io.OutputStream; import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; /** * diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java index 03e1ff61d8..5e3117d2e4 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java @@ -115,6 +115,8 @@ public class HgRepositoryHandler { this.browserResultContext = JAXBContext.newInstance(BrowserResult.class); this.blameResultContext = JAXBContext.newInstance(BlameResult.class); + this.changesetPagingResultContext = + JAXBContext.newInstance(ChangesetPagingResult.class); } catch (JAXBException ex) { @@ -240,7 +242,8 @@ public class HgRepositoryHandler if (TYPE_NAME.equals(type)) { - changesetViewer = new HgChangesetViewer(this, repository); + changesetViewer = new HgChangesetViewer(this, repository, + changesetPagingResultContext); } else { @@ -310,6 +313,27 @@ public class HgRepositoryHandler return TYPE; } + /** + * Method description + * + * + * @param repositoryDirectory + * + * @return + */ + HgChangesetViewer getChangesetViewer(File repositoryDirectory) + { + AssertUtil.assertIsNotNull(repositoryDirectory); + + if (!repositoryDirectory.isDirectory()) + { + throw new IllegalStateException("directory not found"); + } + + return new HgChangesetViewer(this, repositoryDirectory, + changesetPagingResultContext); + } + //~--- methods -------------------------------------------------------------- /** @@ -552,4 +576,7 @@ public class HgRepositoryHandler /** Field description */ private JAXBContext browserResultContext; + + /** Field description */ + private JAXBContext changesetPagingResultContext; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHookEvent.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHookEvent.java index 43f31694c5..7020f9b435 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHookEvent.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHookEvent.java @@ -110,8 +110,13 @@ public class HgRepositoryHookEvent extends AbstractRepositoryHookEvent logger.debug("load {}changesets for hook {}", pendingString, type); } - changesets = createChangesetViewer().getChangesets(startRev, REV_TIP, - pending); + ChangesetPagingResult result = + createChangesetViewer().getChangesets(startRev, REV_TIP, pending); + + if (result != null) + { + changesets = result.getChangesets(); + } } catch (IOException ex) { @@ -147,8 +152,7 @@ public class HgRepositoryHookEvent extends AbstractRepositoryHookEvent File directory = handler.getConfig().getRepositoryDirectory(); File repositoryDirectory = new File(directory, repositoryName); - return new HgChangesetViewer(handler, - repositoryDirectory.getAbsolutePath()); + return handler.getChangesetViewer(repositoryDirectory); } //~--- fields --------------------------------------------------------------- diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java index 47d5cf426b..6bdc56d72b 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java @@ -39,7 +39,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.SCMContext; -import sonia.scm.repository.BrowserResult; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.Repository; @@ -53,6 +52,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.HashMap; import java.util.Map; import javax.xml.bind.JAXBContext; @@ -96,16 +96,15 @@ public class HgUtil * * * @param handler - * @param repository - * @param revision - * @param path + * @param directory + * @param extraEnv * * @return * * @throws IOException */ public static Process createPythonProcess(HgRepositoryHandler handler, - Repository repository, String revision, String path) + File directory, Map extraEnv) throws IOException { HgConfig config = handler.getConfig(); @@ -113,12 +112,8 @@ public class HgUtil Map env = pb.environment(); env.put(ENV_PYTHON_PATH, Util.nonNull(config.getPythonPath())); - - String directory = handler.getDirectory(repository).getAbsolutePath(); - - env.put(ENV_REPOSITORY_PATH, directory); - env.put(ENV_REVISION, getRevision(revision)); - env.put(ENV_PATH, Util.nonNull(path)); + env.put(ENV_REPOSITORY_PATH, directory.getAbsolutePath()); + env.putAll(extraEnv); return pb.start(); } @@ -145,21 +140,6 @@ public class HgUtil return new File(cgiDirectory, CGI_NAME); } - /** - * Method description - * - * - * @param revision - * - * @return - */ - public static String getRevision(String revision) - { - return Util.isEmpty(revision) - ? REVISION_TIP - : revision; - } - /** * Method description * @@ -168,23 +148,21 @@ public class HgUtil * @param context * @param scriptResource * @param handler - * @param repository - * @param revision - * @param path + * @param directory + * @param extraEnv * @param * * @return * * @throws IOException */ - public static T getResultFromScript(Class resultType, JAXBContext context, - String scriptResource, - HgRepositoryHandler handler, - Repository repository, String revision, - String path) + public static T getResultFromScript(Class resultType, + JAXBContext context, String scriptResource, + HgRepositoryHandler handler, File directory, + Map extraEnv) throws IOException { - Process p = createPythonProcess(handler, repository, revision, path); + Process p = createPythonProcess(handler, directory, extraEnv); T result = null; InputStream resource = null; InputStream input = null; @@ -213,4 +191,79 @@ public class HgUtil return result; } + + /** + * Method description + * + * + * @param resultType + * @param context + * @param scriptResource + * @param handler + * @param repository + * @param extraEnv + * @param + * + * @return + * + * @throws IOException + */ + public static T getResultFromScript(Class resultType, + JAXBContext context, String scriptResource, + HgRepositoryHandler handler, Repository repository, + Map extraEnv) + throws IOException + { + File directory = handler.getDirectory(repository); + + return getResultFromScript(resultType, context, scriptResource, handler, + directory, extraEnv); + } + + /** + * Method description + * + * + * @param resultType + * @param context + * @param scriptResource + * @param handler + * @param repository + * @param revision + * @param path + * @param + * + * @return + * + * @throws IOException + */ + public static T getResultFromScript(Class resultType, + JAXBContext context, String scriptResource, + HgRepositoryHandler handler, Repository repository, String revision, + String path) + throws IOException + { + Map extraEnv = new HashMap(); + + extraEnv.put(ENV_REVISION, getRevision(revision)); + extraEnv.put(ENV_PATH, Util.nonNull(path)); + + return getResultFromScript(resultType, context, scriptResource, handler, + repository, extraEnv); + } + + /** + * Method description + * + * + * @param revision + * + * @return + */ + public static String getRevision(String revision) + { + return Util.isEmpty(revision) + ? REVISION_TIP + : revision; + } } diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/hglog.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/hglog.py new file mode 100644 index 0000000000..eebee94f60 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/hglog.py @@ -0,0 +1,131 @@ +# +# 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 +# +# + +import sys, os + +pythonPath = os.environ['SCM_PYTHON_PATH'] + +if len(pythonPath) > 0: + pathParts = pythonPath.split(os.pathsep) + for i in range(len(pathParts)): + sys.path.insert(i, pathParts[i]) + +from mercurial import hg, ui, commands +from mercurial.node import hex +from xml.sax.saxutils import escape +import datetime, time + +repositoryPath = os.environ['SCM_REPOSITORY_PATH'] +repo = hg.repository(ui.ui(), path = repositoryPath) + +start = os.environ['SCM_REVISION_START'] +limit = os.environ['SCM_REVISION_LIMIT'] + +total = len(repo) +limit = int(limit) + +try: + start = int(start) + startRev = total - start - 1 + if startRev < 0: + startRev = 0 +except ValueError: + startRev = repo[start].rev() + +endRev = startRev - limit +if endRev < -1: + endRev = -1 + +# header +print '' +print ' ' + str(total) + '' +print ' ' + +# changesets +for i in range(startRev, endRev, -1): + ctx = repo[i] + time = int(ctx.date()[0]) * 1000 + branch = ctx.branch() + tags = ctx.tags() + status = repo.status(ctx.p1().node(), ctx.node()) + mods = status[0] + added = status[1] + deleted = status[2] + + print ' ' + print ' ' + str(i) + ':' + hex(ctx.node()[:6]) + '' + print ' ' + escape(ctx.user()) + '' + print ' ' + escape(ctx.description()) + '' + print ' ' + str(time).split('.')[0] + '' + + # branches + if branch != 'default': + print ' ' + print ' ' + branch + '' + print ' ' + + # tags + if tags: + print ' ' + for t in tags: + print ' ' + t + '' + print ' ' + + # modifications + print ' ' + + # files added + if added: + print ' ' + for add in added: + print ' ' + add + '' + print ' ' + + # files modified + if mods: + print ' ' + for mod in mods: + print ' ' + mod + '' + print ' ' + + # files deleted + if deleted: + print ' ' + for dele in deleted: + print ' ' + dele + '' + print ' ' + + print ' ' + print ' ' + +# footer +print ' ' +print ''