mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 23:45:44 +01:00
improve performance of mercurial changesetviewer
This commit is contained in:
@@ -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 =
|
||||
"\"<changeset>"
|
||||
+ "<id>{rev}:{node|short}</id>"
|
||||
+ "<author>{author|escape}</author>"
|
||||
+ "<description>{desc|escape}</description>"
|
||||
+ "<date>{date|isodatesec}</date>"
|
||||
+ "<tags>{tags}</tags>"
|
||||
+ "<branches>{branches}</branches>"
|
||||
+ "<files-added>{file_adds}</files-added>"
|
||||
+ "<files-mods>{file_mods}</files-mods>"
|
||||
+ "<files-dels>{file_dels}</files-dels>"
|
||||
+ "</changeset>\"";
|
||||
//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<Changeset> 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<Changeset> getChangesets(String startRev, String endRev,
|
||||
public ChangesetPagingResult getChangesets(String startRev, String limit,
|
||||
boolean pending)
|
||||
throws IOException
|
||||
{
|
||||
List<Changeset> 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<String, String> env = new HashMap<String, String>();
|
||||
|
||||
env.put(ENV_PENDING, repositoryPath);
|
||||
command.setEnvironment(env);
|
||||
}
|
||||
|
||||
CommandResult result = command.execute();
|
||||
|
||||
if (result.isSuccessfull())
|
||||
{
|
||||
StringBuilder sb = new StringBuilder("<changesets>");
|
||||
|
||||
sb.append(result.getOutput()).append("</changesets>");
|
||||
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<String, String> env = new HashMap<String, String>();
|
||||
|
||||
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<Changeset> 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;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 ---------------------------------------------------------------
|
||||
|
||||
@@ -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<String, String> extraEnv)
|
||||
throws IOException
|
||||
{
|
||||
HgConfig config = handler.getConfig();
|
||||
@@ -113,12 +112,8 @@ public class HgUtil
|
||||
Map<String, String> 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 <T>
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static <T> T getResultFromScript(Class<T> resultType, JAXBContext context,
|
||||
String scriptResource,
|
||||
HgRepositoryHandler handler,
|
||||
Repository repository, String revision,
|
||||
String path)
|
||||
public static <T> T getResultFromScript(Class<T> resultType,
|
||||
JAXBContext context, String scriptResource,
|
||||
HgRepositoryHandler handler, File directory,
|
||||
Map<String, String> 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 <T>
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static <T> T getResultFromScript(Class<T> resultType,
|
||||
JAXBContext context, String scriptResource,
|
||||
HgRepositoryHandler handler, Repository repository,
|
||||
Map<String, String> 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 <T>
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static <T> T getResultFromScript(Class<T> resultType,
|
||||
JAXBContext context, String scriptResource,
|
||||
HgRepositoryHandler handler, Repository repository, String revision,
|
||||
String path)
|
||||
throws IOException
|
||||
{
|
||||
Map<String, String> extraEnv = new HashMap<String, String>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user