823N/A/*
823N/A * CDDL HEADER START
823N/A *
823N/A * The contents of this file are subject to the terms of the
823N/A * Common Development and Distribution License (the "License").
823N/A * You may not use this file except in compliance with the License.
823N/A *
823N/A * See LICENSE.txt included in this distribution for the specific
823N/A * language governing permissions and limitations under the License.
823N/A *
823N/A * When distributing Covered Code, include this CDDL HEADER in each
823N/A * file and include the License file at LICENSE.txt.
823N/A * If applicable, add the following below this CDDL HEADER, with the
823N/A * fields enclosed by brackets "[]" replaced with your own identifying
823N/A * information: Portions Copyright [yyyy] [name of copyright owner]
823N/A *
823N/A * CDDL HEADER END
823N/A */
823N/A
823N/A/*
1248N/A * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
823N/A */
823N/Apackage org.opensolaris.opengrok.history;
823N/A
823N/Aimport java.io.BufferedInputStream;
823N/Aimport java.io.BufferedReader;
823N/Aimport java.io.ByteArrayInputStream;
823N/Aimport java.io.ByteArrayOutputStream;
823N/Aimport java.io.File;
823N/Aimport java.io.IOException;
823N/Aimport java.io.InputStream;
823N/Aimport java.util.ArrayList;
823N/Aimport java.util.List;
1327N/Aimport java.util.logging.Logger;
823N/Aimport java.util.regex.Matcher;
823N/Aimport java.util.regex.Pattern;
1327N/A
1462N/Aimport org.opensolaris.opengrok.configuration.Configuration;
823N/Aimport org.opensolaris.opengrok.util.Executor;
1195N/Aimport org.opensolaris.opengrok.util.IOUtils;
823N/A
823N/A/**
823N/A * Access to a Monotone repository.
823N/A *
823N/A * @author Trond Norbye
823N/A */
823N/Apublic class MonotoneRepository extends Repository {
823N/A
1473N/A private static final Logger logger =
1473N/A Logger.getLogger(MonotoneRepository.class.getName());
823N/A private static final long serialVersionUID = 1L;
1182N/A /** The property name used to obtain the client command for this repository. */
1190N/A public static final String CMD_PROPERTY_KEY =
1462N/A Configuration.PROPERTY_KEY_PREFIX + "history.Monotone";
1182N/A /** The command to use to access the repository if none was given explicitly */
1182N/A public static final String CMD_FALLBACK = "mnt";
823N/A
1461N/A /**
1473N/A * Create a new instance of type {@code Monotone}.
1461N/A */
823N/A public MonotoneRepository() {
823N/A type = "Monotone";
823N/A datePattern = "yyyy-MM-dd'T'hh:mm:ss";
823N/A }
823N/A
1461N/A /**
1461N/A * {@inheritDoc}
1461N/A */
1461N/A @SuppressWarnings("resource")
823N/A @Override
1190N/A public InputStream getHistoryGet(String parent, String basename, String rev)
1183N/A {
823N/A InputStream ret = null;
823N/A
823N/A File directory = new File(directoryName);
823N/A
823N/A Process process = null;
823N/A InputStream in = null;
823N/A String revision = rev;
823N/A
823N/A try {
1182N/A String filename = (new File(parent, basename)).getCanonicalPath()
1182N/A .substring(directoryName.length() + 1);
1182N/A ensureCommand(CMD_PROPERTY_KEY, CMD_FALLBACK);
1473N/A String argv[] = { cmd, "cat", "-r", revision, filename };
823N/A process = Runtime.getRuntime().exec(argv, null, directory);
823N/A
823N/A ByteArrayOutputStream out = new ByteArrayOutputStream();
823N/A byte[] buffer = new byte[32 * 1024];
823N/A in = process.getInputStream();
823N/A int len;
823N/A
823N/A while ((len = in.read(buffer)) != -1) {
823N/A if (len > 0) {
823N/A out.write(buffer, 0, len);
823N/A }
823N/A }
823N/A
823N/A ret = new BufferedInputStream(new ByteArrayInputStream(out.toByteArray()));
823N/A } catch (Exception exp) {
1327N/A logger.warning("Failed to get history: " + exp.getMessage());
823N/A } finally {
1195N/A IOUtils.close(in);
823N/A // Clean up zombie-processes...
823N/A if (process != null) {
823N/A try {
823N/A process.exitValue();
823N/A } catch (IllegalThreadStateException exp) {
823N/A // the process is still running??? just kill it..
823N/A process.destroy();
823N/A }
823N/A }
823N/A }
823N/A
823N/A return ret;
823N/A }
823N/A
1473N/A /**
1473N/A * Get an executor to be used for retrieving the history log for the given
1473N/A * file.
1473N/A *
1473N/A * @param file file for which history is to be retrieved (canonical path
1473N/A * incl. source root).
1473N/A * @return An Executor ready to be started
1473N/A */
823N/A Executor getHistoryLogExecutor(File file, String changeset)
1473N/A throws IOException
1473N/A {
1016N/A String abs = file.getCanonicalPath();
823N/A String filename = "";
823N/A if (abs.length() > directoryName.length()) {
823N/A filename = abs.substring(directoryName.length() + 1);
823N/A }
823N/A
823N/A List<String> cmd = new ArrayList<String>();
1182N/A ensureCommand(CMD_PROPERTY_KEY, CMD_FALLBACK);
1182N/A cmd.add(this.cmd);
823N/A cmd.add("log");
823N/A
823N/A if (changeset != null) {
823N/A cmd.add("--to");
823N/A cmd.add(changeset);
823N/A }
823N/A
823N/A cmd.add("--no-graph");
823N/A cmd.add("--no-merges");
1437N/A cmd.add("--no-format-dates");
823N/A cmd.add(filename);
823N/A
823N/A return new Executor(cmd, new File(directoryName));
823N/A }
823N/A /** Pattern used to extract author/revision from hg annotate. */
1238N/A private static final Pattern ANNOTATION_PATTERN =
1473N/A Pattern.compile("^(\\w+)\\p{Punct}\\p{Punct} by (\\S+)");
823N/A
823N/A /**
1473N/A * {@inheritDoc}
823N/A */
823N/A @Override
823N/A public Annotation annotate(File file, String revision) throws IOException {
823N/A ArrayList<String> cmd = new ArrayList<String>();
1182N/A ensureCommand(CMD_PROPERTY_KEY, CMD_FALLBACK);
1182N/A cmd.add(this.cmd);
823N/A cmd.add("annotate");
1437N/A cmd.add(getQuietOption());
823N/A if (revision != null) {
823N/A cmd.add("-r");
823N/A cmd.add(revision);
823N/A }
823N/A cmd.add(file.getName());
823N/A File directory = new File(directoryName);
823N/A
823N/A Executor executor = new Executor(cmd, directory);
823N/A if (executor.exec() != 0) {
823N/A throw new IOException(executor.getErrorString());
823N/A }
823N/A
823N/A BufferedReader in = null;
823N/A Annotation ret = null;
823N/A try {
823N/A in = new BufferedReader(executor.getOutputReader());
823N/A ret = new Annotation(file.getName());
823N/A String line;
823N/A String author = null;
823N/A String rev = null;
1461N/A Matcher matcher = ANNOTATION_PATTERN.matcher("");
823N/A while ((line = in.readLine()) != null) {
1461N/A matcher.reset(line);
823N/A if (matcher.find()) {
823N/A rev = matcher.group(1);
823N/A author = matcher.group(2);
1384N/A ret.addLine(rev, author);
823N/A } else {
1384N/A ret.addLine(rev, author);
823N/A }
823N/A }
823N/A } finally {
1195N/A IOUtils.close(in);
823N/A }
823N/A return ret;
823N/A }
823N/A
1461N/A /**
1461N/A * {@inheritDoc}
1461N/A */
823N/A @Override
823N/A public boolean fileHasAnnotation(File file) {
823N/A return true;
823N/A }
823N/A
1461N/A /**
1461N/A * {@inheritDoc}
1461N/A */
823N/A @Override
823N/A public void update() throws IOException {
823N/A File directory = new File(directoryName);
1182N/A ensureCommand(CMD_PROPERTY_KEY, CMD_FALLBACK);
823N/A
823N/A List<String> cmd = new ArrayList<String>();
1182N/A cmd.add(this.cmd);
823N/A cmd.add("pull");
1437N/A cmd.add(getQuietOption());
823N/A Executor executor = new Executor(cmd, directory);
823N/A if (executor.exec() != 0) {
823N/A throw new IOException(executor.getErrorString());
823N/A }
823N/A
823N/A cmd.clear();
1182N/A cmd.add(this.cmd);
823N/A cmd.add("update");
1437N/A cmd.add(getQuietOption());
823N/A executor = new Executor(cmd, directory);
823N/A if (executor.exec() != 0) {
823N/A throw new IOException(executor.getErrorString());
823N/A }
823N/A }
823N/A
1461N/A /**
1461N/A * {@inheritDoc}
1461N/A */
823N/A @Override
823N/A public boolean fileHasHistory(File file) {
823N/A return true;
823N/A }
823N/A
1473N/A /**
1473N/A * {@inheritDoc}
1473N/A */
823N/A @Override
1473N/A public boolean isRepositoryFor(File file) {
823N/A File f = new File(file, "_MTN");
823N/A return f.exists() && f.isDirectory();
823N/A }
823N/A
1461N/A /**
1461N/A * {@inheritDoc}
1461N/A */
823N/A @Override
823N/A public boolean isWorking() {
1182N/A if (working == null) {
1182N/A ensureCommand(CMD_PROPERTY_KEY, CMD_FALLBACK);
1248N/A working = checkCmd(cmd, "--help");
1182N/A }
1182N/A return working.booleanValue();
823N/A }
823N/A
1473N/A /**
1473N/A * {@inheritDoc}
1473N/A */
823N/A @Override
1473N/A public boolean hasHistoryForDirectories() {
823N/A return true;
823N/A }
823N/A
1473N/A /**
1473N/A * {@inheritDoc}
1473N/A */
823N/A @Override
1473N/A public History getHistory(File file) throws HistoryException {
823N/A return getHistory(file, null);
823N/A }
823N/A
1473N/A /**
1473N/A * {@inheritDoc}
1473N/A */
823N/A @Override
1473N/A public History getHistory(File file, String sinceRevision)
1473N/A throws HistoryException
1473N/A {
823N/A return new MonotoneHistoryParser(this).parse(file, sinceRevision);
823N/A }
1437N/A
1461N/A private String quietOption;
1461N/A
1437N/A private String getQuietOption() {
1461N/A if (quietOption == null) {
1461N/A quietOption = System.getProperty(DEPRECATED_KEY, null);
1461N/A quietOption = quietOption != null && Boolean.parseBoolean(quietOption)
1461N/A ? "--reallyquiet"
1461N/A : "--quiet --quiet";
1437N/A }
1461N/A return quietOption;
1437N/A }
1437N/A
1461N/A /**
1461N/A * The boolean system property to set to {@code true} if the deprecated
1461N/A * quiet option {@code --reallyquiet} should be used instead of
1461N/A * {@code --quiet --quiet}.
1461N/A */
1437N/A public static final String DEPRECATED_KEY =
1462N/A Configuration.PROPERTY_KEY_PREFIX + "history.monotone.deprecated";
823N/A}