| /******************************************************************************* |
| * 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://"; |
| } |
| } |