com.ironiacorp.scm.subversion.SubversionRepositoryTransaction.java Source code

Java tutorial

Introduction

Here is the source code for com.ironiacorp.scm.subversion.SubversionRepositoryTransaction.java

Source

/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    
Copyright (C) 2005 Marco Aurlio Graciotto Silva <magsilva@gmail.com>
*/

package com.ironiacorp.scm.subversion;

import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.tigris.subversion.javahl.*;
import org.tigris.subversion.javahl.Status;

import com.ironiacorp.io.IoUtil;
import com.ironiacorp.scm.ConfigurationItem;
import com.ironiacorp.scm.RepositoryTransaction;
import com.ironiacorp.scm.RepositoryTransactionError;
import com.ironiacorp.scm.Repository;
import com.ironiacorp.scm.TransactionStatus;

/**
 * Subversion (http://subversion.tigris.org) is a software configuration
 * manager inspired by CVS. It copies much of CVS's functionalities, but
 * without the glitches (directories are versioned too, for example).
 */
public class SubversionRepositoryTransaction extends RepositoryTransaction {
    /**
    * Commons Logging instance.
    */
    private static Log log = LogFactory.getLog(SubversionRepositoryTransaction.class);

    /**
     * Subversion error handler.
     */
    class SubversionNotifier implements Notify2 {
        /**
         * Handler for Subversion notifications.
         *
         * @param info everything to know about this event
         */
        public void onNotify(NotifyInformation info) {
            log.info(info);
            log.debug(String.format("[%1$d] %2$d (%1$s)", info.getRevision(), info.getAction(), info.getPath()));
        }
    }

    /**
     * Default Subversion error handler.
     */
    private final Notify2 notifier = new SubversionNotifier();

    /**
     * The username used to login in the Subversion repository.
     */
    private String username;

    /**
     * The password for the username.
     */
    private String password;

    /**
     * The repository URL.
     */
    private String url;

    private String rootDir;

    /**
     * The real Subversion client. It comes from the Javahl bindindgs, that use
     * JNI to access the native Subversion libraries. The documentation says
     * this class is not thread-safe. Maybe should be a good idea to use the
     * thread-safe SVNClientSynchronized or, at least, investigate how the
     * threading issues can affect this class.
     */
    private SVNClientInterface client;

    /**
     * Counter used by createTempFile.
     */
    private static AtomicLong counter = new AtomicLong(Double.doubleToLongBits(Math.random()));

    /**
    * Create the repository transaction to access the Subversion.
    * 
    * @param project ProjectIF to use to retrieve the configuration items.
    * @param user UserIF to be used to access the repository.
    * 
    * @throws RepositoryTransactionError If the URL is invalid or the user's
    * configuration directory could not be created.
    */
    public SubversionRepositoryTransaction(Repository repository) {
        super(repository);
        client = new SVNClientSynchronized();
        rootDir = "/";
        try {
            client.setConfigDirectory(rootDir);
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.transactionInitialization", e);
        }
        setUrl(repository.getLocation());
        // setUsername( repository.getUsername() );
        // setPassword( repository.getPassword() );

        client.notification2(notifier);
    }

    /**
     * Set the user's password (as required by Subversion).
     * 
     * @param password The password used to login in the Subversion repository.
     */
    private void setPassword(String password) {
        if (password != null) {
            this.password = password;
            client.password(this.password);
        }
    }

    /**
     * Set the username (as required by Subversion).
     * 
     * @param username The username used to login in the Subversion repository.
     */
    private void setUsername(String username) {
        if (username != null) {
            this.username = username;
            client.username(this.username);
        }
    }

    /**
     * Set the URL used to access the repository.
     * 
     * @param url The address that will be used to access the repository.
     * 
     * @throws RepositoryTransactionError The URL is invalid.
     */
    private void setUrl(String url) {
        try {
            new URL(url);
        } catch (MalformedURLException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.invalidRepositoryURL", e);
        }
        this.url = url;
    }

    /**
     * Checkout a recent (HEAD) copy of the project's repository.
     * 
     * @throws RepositoryTransactionError If an error occurred when checking out
     * the files.
     */
    public void checkout() {
        super.checkout();
        _checkout(Revision.HEAD);
    }

    /**
     * Checkout a copy of the project's repository at the given version.
     * 
     * @param version The version (the Subversion revision) to be retrieved.
     * 
     * @throws RepositoryTransactionError If an error occurred when checking out
     * the files.
     */
    public void checkout(String version) {
        super.checkout(version);
        Revision revision = Revision.getInstance(Long.parseLong(version));
        _checkout(revision);
    }

    /**
     * Checkout a copy of the project's repository at the given revision.
     * 
     * @param revision The revision to be retrieved.
     * 
     * @throws RepositoryTransactionError If an error occurred when checking out
     * the files.
     */
    private void _checkout(Revision revision) {
        try {
            client.checkout(url, workdir.toString(), revision, true);
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.checkout", e);
        }
    }

    /**
     * Add a file or directory to the project's repository. If a directory is
     * used as parameter, all the files and subdirs are added. 
     * 
     * @param path The file or directory to be added.
     * 
     * @throws RepositoryTransactionError If an error occurred when adding the
     * file(s).
     */
    public void add(String path) {
        super.add(path);
        _add(path, true);
    }

    /**
     * Add a file or directory to the project's repository. If a directory is
     * used as parameter and "recurse" is true, all the files and subdirs will
     * be added. 
     * 
     * @param path The file or directory to be added.
     * @param recurse If the files and subdirectories within the directory
     * must be added.
     * 
     * @throws RepositoryTransactionError If an error occurred when adding the
     * file(s).
     */
    public void add(String path, boolean recurse) {
        super.add(path);
        _add(path, recurse);
    }

    /**
     * Add a file or directory to the project's repository. If a directory is
     * used as parameter and "recurse" is true, all the files and subdirs will
     * be added. 
     * 
     * @param path The file or directory to be added.
     * @param recurse If the files and subdirectories within the directory
     * must be added.
     * 
     * @throws RepositoryTransactionError If an error occurred when adding the
     * file(s).
     */
    private void _add(String path, boolean recurse) {
        try {
            client.add(path, recurse);
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.add", e);
        }
    }

    /**
     * Remove a file or directory from the project's repository. If a directory
     * is used as parameter, all the files and subdirs are removed. 
     * 
     * @param path The file or directory to be removed.
     * 
     * @throws RepositoryTransactionError If an error occurred when removing
     * the file(s).
     */
    public void remove(String path) {
        super.remove(path);
        try {
            String[] paths = { path };
            client.remove(paths, "", true);
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.remove", e);
        }
    }

    /**
     * Revert a file or directory to its previous stable state in the project's
     * repository. If a directory is used as parameter, all the files and
     * subdirs are reverted. 
     * 
     * @param path The file or directory to be reverted.
     * 
     * @throws RepositoryTransactionError If an error occurred when reverting
     * the file(s).
     */
    public void revert(String path) {
        super.revert(path);
        try {
            client.revert(path, true);
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.revert", e);
        }
    }

    /**
     * Get metadata for every file in the given path. If a directory is used as
     * parameter, metadata for every files and subdirs is retrieved. 
     * 
     * @param path The file or directory to have its metadata retrieved.
     * 
     * @return The metadata for every file in the path.
     * 
     * @throws RepositoryTransactionError If an error occurred when retrieving
     * the metadata.
     */
    public ConfigurationItem[] info(String path) {
        super.info(path);
        try {
            ArrayList<ConfigurationItem> items = new ArrayList<ConfigurationItem>();
            org.tigris.subversion.javahl.Status[] overallStatus = client.status(path, true, false, true);

            for (Status status : overallStatus) {
                Info info = client.info(status.getPath());
                ConfigurationItem ci = new ConfigurationItem(info.getName());
                ci.setVersion(Long.toString(info.getCopyRev()));
                ci.setAuthor(info.getAuthor());
                switch (status.getTextStatus()) {
                case StatusKind.added:
                    ci.setStatus(ConfigurationItem.Status.ADDED);
                    break;
                case StatusKind.conflicted:
                    ci.setStatus(ConfigurationItem.Status.CONFLICTED);
                    break;
                case StatusKind.deleted:
                    ci.setStatus(ConfigurationItem.Status.REMOVED);
                    break;
                case StatusKind.external:
                    ci.setStatus(ConfigurationItem.Status.EXTERNAL);
                    break;
                case StatusKind.ignored:
                    ci.setStatus(ConfigurationItem.Status.IGNORED);
                    break;
                case StatusKind.incomplete:
                    ci.setStatus(ConfigurationItem.Status.INCOMPLETE);
                    break;
                case StatusKind.merged:
                    ci.setStatus(ConfigurationItem.Status.MERGED);
                    break;
                case StatusKind.missing:
                    ci.setStatus(ConfigurationItem.Status.MISSING);
                    break;
                case StatusKind.modified:
                    ci.setStatus(ConfigurationItem.Status.MODIFIED);
                    break;
                case StatusKind.none:
                    ci.setStatus(ConfigurationItem.Status.NORMAL);
                    break;
                case StatusKind.normal:
                    ci.setStatus(ConfigurationItem.Status.NORMAL);
                    break;
                case StatusKind.obstructed:
                    ci.setStatus(ConfigurationItem.Status.MODIFIED);
                    break;
                case StatusKind.replaced:
                    ci.setStatus(ConfigurationItem.Status.MODIFIED);
                    break;
                case StatusKind.unversioned:
                    ci.setStatus(ConfigurationItem.Status.UNVERSIONED);
                    break;
                default:
                    ci.setStatus(ConfigurationItem.Status.NORMAL);
                }
                items.add(ci);
            }
            return items.toArray(new ConfigurationItem[0]);
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.info", e);
        }
    }

    /**
     * Update a file or directory to its latest version (the HEAD from the
     * project's repository). If a directory is used as parameter, all the
     * files and subdirs are updated. 
     * 
     * @param path The file or directory to be updated.
     * 
     * @throws RepositoryTransactionError If an error occurred when updating
     * the file(s).
     */
    public void update(String path) {
        super.update(path);
        _update(path, Revision.HEAD);
    }

    /**
     * Update a file or directory to the request version. If a directory is
     * used as parameter, all the files and subdirs are updated. 
     * 
     * @param path The file or directory to be updated.
     * @param version The version to update to.
     * 
     * @throws RepositoryTransactionError If an error occurred when updating
     * the file(s).
     */
    public void update(String path, String version) {
        super.update(path, version);
        Revision revision = Revision.getInstance(Long.parseLong(version));
        _update(path, revision);
    }

    /**
     * Update a file or directory to the requested revision. If a directory is
     * used as parameter, all the files and subdirs are updated. 
     * 
     * @param path The file or directory to be updated.
     * @param revision The revision to update to.
     * 
     * @throws RepositoryTransactionError If an error occurred when updating
     * the file(s).
     */
    private void _update(String path, Revision revision) {
        try {
            client.update(path, revision, true);
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.update", e);
        }
    }

    /**
     * Create a branch. 
     * 
     * @param srcPath The source directory (the directory that will be
     * branched).
     * @param destPath The branch directory that will be created.
     * 
     * @throws RepositoryTransactionError If an error occurred when creating the
     * branch.
     */
    public void branch(String srcPath, String destPath) {
        super.branch(srcPath, destPath);
        try {
            client.copy(srcPath, destPath, "", Revision.HEAD);
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.branch", e);
        }
    }

    /**
     * Diff two files or directories. 
     * 
     * @param srcPath The source directory.
     * @param destPath The destination directory.
     * 
     * @return The name of the file holding the diff.
     * 
     * @throws RepositoryTransactionError If an error occurred when comparing
     * the files.
     */
    public String diff(String srcPath, String destPath) {
        super.diff(srcPath, destPath);
        try {
            String destFile = workdir + File.separator + "diff" + getId() + "-" + counter.getAndIncrement()
                    + ".patch";
            client.diff(srcPath, Revision.HEAD, destPath, Revision.HEAD, destFile, true);
            return destFile;
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.diff", e);
        }
    }

    /**
     * Diff different versions of a files or directory.  
     * 
     * @param path The file or directory whose versions will be compared.
     * @param version1 The source version.
     * @param version2 The destination version.
     * 
     * @return The name of the file holding the diff.
     * 
     * @throws RepositoryTransactionError If an error occurred when comparing
     * the files.
     */
    public String diff(String path, String version1, String version2) {
        super.diff(path, version1, version2);
        return diff(path, version1, path, version2);
    }

    /**
     * Diff different versions of different files or directory.  
     * 
     * @param srcPath The source file or directory whose versions will be
     * compared.
     * @param destPath The destination file or directory whose versions will be
     * compared.
     * @param version1 The source version.
     * @param version2 The destination version.
     * 
     * @return The name of the file holding the diff.
     * 
     * @throws RepositoryTransactionError If an error occurred when comparing
     * the files.
     */
    public String diff(String srcPath, String version1, String destPath, String version2) {
        super.diff(srcPath, version1, destPath, version2);
        try {
            Revision revision1 = Revision.getInstance(Long.parseLong(version1));
            Revision revision2 = Revision.getInstance(Long.parseLong(version2));
            String destFile = workdir + File.separator + "diff" + getId() + "-" + counter.getAndIncrement()
                    + ".patch";
            client.diff(srcPath, revision1, destPath, revision2, destFile, true);
            return destFile;
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.diff", e);
        }
    }

    /**
     * Commit the modifications made to the workcopy.
     * 
     * @param changelog Description of the changes made to the repository.
     * 
     * @throws RepositoryTransactionError If an error occurred when commiting
     * the files.
     */
    public void commit(String changelog) {
        try {
            String[] paths = { workdir.toString() };
            client.resolved(workdir.toString(), true);
            client.commit(paths, changelog, true);
            client.dispose();
            fs.removeDir(workdir);
        } catch (ClientException e) {
            log.debug(e.getMessage());
            throw new RepositoryTransactionError("exception.repositoryTransaction.commit", e);
        }
        super.commit(changelog);
    }

    /**
     * Abort (rollback) the modifications made to the workcopy.
     */
    public void abort() {
        client.dispose();
        super.abort();
    }

    public TransactionStatus getStatus() {
        // TODO Auto-generated method stub
        return null;
    }
}