blob: e91a32a850fdeafcf618d892013299ff5eb1cc85 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2015 The University of York.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 3.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-3.0
*
* Contributors:
* Konstantinos Barmpis - initial API and implementation
* Ossmeter team (https://opensourceprojects.eu/p/ossmeter) - SVN delta computation algorithm
* Antonio Garcia-Dominguez - do not blacklist .zip files, allow any valid URL, minor clean up
******************************************************************************/
package org.eclipse.hawk.svn;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.hawk.core.IConsole;
import org.eclipse.hawk.core.ICredentialsStore;
import org.eclipse.hawk.core.ICredentialsStore.Credentials;
import org.eclipse.hawk.core.IModelIndexer;
import org.eclipse.hawk.core.IVcsManager;
import org.eclipse.hawk.core.VcsChangeType;
import org.eclipse.hawk.core.VcsCommit;
import org.eclipse.hawk.core.VcsCommitItem;
import org.eclipse.hawk.core.VcsRepositoryDelta;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.SVNRevision;
public class SvnManager implements IVcsManager {
private IConsole console;
private boolean isActive = false;
private String repositoryURL;
private String username;
private String password;
private IModelIndexer indexer;
private boolean isFrozen = false;
private SVNRepository svnRepository;
/*
* TODO we can't blacklist .zip as we need support for zipped Modelio
* projects - should we use a different extension (".mzip", perhaps?),or a
* whitelist (".modelio.zip"?)?
*/
private static final Set<String> EXTENSION_BLACKLIST = new HashSet<>(
Arrays.asList(".png", ".jpg", ".bmp", ".jar", ".gz", ".tar"));
@Override
public void init(String vcsloc, IModelIndexer indexer) throws Exception {
console = indexer.getConsole();
this.repositoryURL = vcsloc;
this.indexer = indexer;
}
@Override
public void run() throws Exception {
try {
final ICredentialsStore credStore = indexer.getCredentialsStore();
if (username != null) {
// The credentials were provided by a previous setCredentials
// call: retry the change to the credentials store.
setCredentials(username, password, credStore);
} else {
final Credentials credentials = credStore.get(repositoryURL);
if (credentials != null) {
this.username = credentials.getUsername();
this.password = credentials.getPassword();
} else {
/*
* If we use null for the default username/password, SVNKit
* will try to use the GNOME keyring in Linux, and that will
* lock up our Eclipse instance in some cases.
*/
console.printerrln("No username/password recorded for the repository " + repositoryURL);
this.username = "";
this.password = "";
}
}
getFirstRevision();
isActive = true;
} catch (Exception e) {
console.printerrln("exception in svnmanager run():");
console.printerrln(e);
}
}
protected SVNRepository getSVNRepository() {
if (svnRepository == null) {
svnRepository = SvnUtil.connectToSVNInstance(repositoryURL, username, password);
}
return svnRepository;
}
@Override
public VcsRepositoryDelta getDelta(String startRevision, String endRevision) throws Exception {
final SVNRepository svnRepository = getSVNRepository();
final List<VcsCommit> commits = new ArrayList<>();
VcsRepositoryDelta delta = new VcsRepositoryDelta(commits);
delta.setManager(this);
final String rootURL = svnRepository.getRepositoryRoot(false).toDecodedString();
final String overlappedURL = makeRelative(rootURL, repositoryURL);
if (startRevision == null && endRevision != startRevision || !startRevision.equals(endRevision)) {
Collection<?> c = svnRepository.log(new String[] { "" }, null,
startRevision == null ? 0 : Long.valueOf(startRevision),
Long.valueOf(endRevision), true, true);
for (Object o : c) {
SVNLogEntry svnLogEntry = (SVNLogEntry) o;
VcsCommit commit = new VcsCommit();
commit.setAuthor(svnLogEntry.getAuthor());
commit.setMessage(svnLogEntry.getMessage());
commit.setRevision(svnLogEntry.getRevision() + "");
commit.setJavaDate(svnLogEntry.getDate());
commits.add(commit);
Map<String, SVNLogEntryPath> changedPaths = svnLogEntry.getChangedPaths();
for (final String path : changedPaths.keySet()) {
SVNLogEntryPath svnLogEntryPath = changedPaths.get(path);
final int lastDotIndex = path.lastIndexOf(".");
if (lastDotIndex <= 1) {
// No extension (index is -1) or path starts by "/." (hidden files in Unix systems): skip
continue;
}
final String ext = path.substring(lastDotIndex, path.length());
if (EXTENSION_BLACKLIST.contains(ext)) {
// Blacklisted extension: skip
continue;
}
if (path.contains(overlappedURL)) {
VcsCommitItem commitItem = new VcsCommitItem();
commit.getItems().add(commitItem);
commitItem.setCommit(commit);
commitItem.setPath(path);
if (svnLogEntryPath.getType() == 'A') {
commitItem.setChangeType(VcsChangeType.ADDED);
} else if (svnLogEntryPath.getType() == 'M') {
commitItem.setChangeType(VcsChangeType.UPDATED);
} else if (svnLogEntryPath.getType() == 'D') {
commitItem.setChangeType(VcsChangeType.DELETED);
} else if (svnLogEntryPath.getType() == 'R') {
commitItem.setChangeType(VcsChangeType.REPLACED);
} else {
console.printerrln("Found unrecognised svn log entry type: " + svnLogEntryPath.getType());
commitItem.setChangeType(VcsChangeType.UNKNOWN);
}
}
}
}
}
return delta;
}
@Override
public String getCurrentRevision() throws Exception {
return getSVNRepository().getLatestRevision() + "";
}
/**
* Cache the log?
*/
@Override
public String getFirstRevision() throws Exception {
final SVNRepository svnRepository = getSVNRepository();
Collection<?> c = svnRepository.log(new String[] { "" }, null, 0, Long.valueOf(getCurrentRevision()), true,
true);
for (Object o : c) {
return String.valueOf(((SVNLogEntry) o).getRevision());
}
return null;
}
private String makeRelative(String base, String extension) {
StringBuilder result = new StringBuilder();
List<String> baseSegments = Arrays.asList(base.split("/"));
String[] extensionSegments = extension.split("/");
for (String ext : extensionSegments) {
if (!baseSegments.contains(ext)) {
result.append(extension.substring(extension.indexOf(ext)));
break;
}
}
return result.toString();
}
@Override
public File importFile(String revision, String path, File temp) {
final SVNRepository svnRepository = getSVNRepository();
final long rev = revision == null ? SVNRevision.HEAD.getNumber() : Long.valueOf(revision);
try {
final SVNNodeKind node = svnRepository.checkPath(path, rev);
if (node == SVNNodeKind.NONE) {
// Path does not exist
return null;
}
} catch (SVNException e1) {
console.printerrln(e1);
return null;
}
try (FileOutputStream fOS = new FileOutputStream(temp)) {
svnRepository.getFile(path, rev, new SVNProperties(), fOS);
return temp;
} catch (Exception e) {
console.printerrln(e);
return null;
}
}
@Override
public boolean isActive() {
return isActive;
}
@Override
public void shutdown() {
repositoryURL = null;
console = null;
}
@Override
public String getLocation() {
return repositoryURL;
}
@Override
public void setCredentials(String username, String password, ICredentialsStore credStore) {
if (username != null && password != null && repositoryURL != null
&& (!username.equals(this.username) || !password.equals(this.password))) {
try {
credStore.put(repositoryURL, new Credentials(username, password));
} catch (Exception e) {
console.printerrln("Could not save new username/password");
console.printerrln(e);
}
}
this.username = username;
this.password = password;
this.svnRepository = null;
}
@Override
public String getHumanReadableName() {
return "SVN Monitor";
}
@Override
public Collection<VcsCommitItem> getDelta(String startRevision) throws Exception {
if (Long.valueOf(startRevision) < 0)
return getDelta(null, getCurrentRevision()).getCompactedCommitItems();
else
return getDelta(startRevision, getCurrentRevision()).getCompactedCommitItems();
}
@Override
public boolean isAuthSupported() {
return true;
}
@Override
public boolean isPathLocationAccepted() {
return false;
}
@Override
public boolean isURLLocationAccepted() {
return true;
}
@Override
public String getRepositoryPath(String rawPath) {
if (rawPath.startsWith(repositoryURL)) {
return rawPath.substring(repositoryURL.length());
}
return rawPath;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return password;
}
@Override
public boolean isFrozen() {
return isFrozen;
}
@Override
public void setFrozen(boolean f) {
isFrozen = f;
}
@Override
public String getDefaultLocation() {
return "svn://";
}
}