/************************************************************************** OmegaT - Computer Assisted Translation (CAT) tool with fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated projects. Copyright (C) 2012 Alex Buloichik 2014 Alex Buloichik, Aaron Madlon-Kay Home page: http://www.omegat.org/ Support center: http://groups.yahoo.com/group/OmegaT/ This file is part of OmegaT. OmegaT 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 3 of the License, or (at your option) any later version. OmegaT 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, see <http://www.gnu.org/licenses/>. **************************************************************************/ package org.omegat.core.team2.impl; import java.io.File; import java.net.SocketException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Logger; import javax.xml.namespace.QName; import org.omegat.core.team2.IRemoteRepository2; import org.omegat.core.team2.ProjectTeamSettings; import org.omegat.util.Log; import org.tmatesoft.svn.core.SVNAuthenticationException; import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil; import org.tmatesoft.svn.core.wc.ISVNOptions; import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNWCUtil; import gen.core.project.RepositoryDefinition; /** * SVN repository connection implementation. * * @author Alex Buloichik (alex73mail@gmail.com) * @author Aaron Madlon-Kay */ public class SVNRemoteRepository2 implements IRemoteRepository2 { private static final Logger LOGGER = Logger.getLogger(SVNRemoteRepository2.class.getName()); RepositoryDefinition config; File baseDirectory; SVNClientManager ourClientManager; List<File> filesForCommit = new ArrayList<File>(); int credentialAskCount; @Override public void init(RepositoryDefinition repo, File dir, ProjectTeamSettings teamSettings) throws Exception { config = repo; baseDirectory = dir; String predefinedUser = repo.getOtherAttributes().get(new QName("svnUsername")); String predefinedPass = repo.getOtherAttributes().get(new QName("svnPassword")); if (predefinedUser == null) { predefinedUser = SVNURL.parseURIEncoded(repo.getUrl()).getUserInfo(); } ISVNOptions options = SVNWCUtil.createDefaultOptions(true); ISVNAuthenticationManager authManager = new SVNAuthenticationManager(repo.getUrl(), predefinedUser, predefinedPass, teamSettings); ourClientManager = SVNClientManager.newInstance(options, authManager); if (baseDirectory.exists()) { ourClientManager.getWCClient().doCleanup(baseDirectory); ourClientManager.getWCClient().doRevert(new File[] { baseDirectory }, SVNDepth.fromRecurse(true), null); } } @Override public String getFileVersion(String file) throws Exception { File f = new File(baseDirectory, file); if (!f.exists()) { return null; } SVNInfo info = ourClientManager.getWCClient().doInfo(f, SVNRevision.BASE); Log.logDebug(LOGGER, "SVN committed revision for file {0} is {1}", file, info.getCommittedRevision() .getNumber()); return Long.toString(info.getCommittedRevision().getNumber()); } @Override public void switchToVersion(String version) throws Exception { Log.logInfoRB("SVN_START", "checkout to " + version); filesForCommit.clear(); SVNURL url = SVNURL.parseURIEncoded(SVNEncodingUtil.autoURIEncode(config.getUrl())); SVNRevision toRev; if (version != null) { toRev = SVNRevision.create(Long.parseLong(version)); } else { toRev = SVNRevision.HEAD; } try { ourClientManager.getUpdateClient().doCheckout(url, baseDirectory, SVNRevision.HEAD, toRev, SVNDepth.INFINITY, false); Log.logInfoRB("SVN_FINISH", "checkout"); } catch (Exception ex) { Log.logErrorRB("SVN_ERROR", "checkout", ex.getMessage()); throw ex; } } @Override public void addForCommit(String path) throws Exception { File f = new File(baseDirectory, path); filesForCommit.add(f); ourClientManager.getWCClient().doAdd(f, true, false, true, SVNDepth.EMPTY, false, true); } @Override public String commit(String[] onVersions, String comment) throws Exception { Log.logInfoRB("SVN_START", "commit"); File[] forCommit = new File[]{baseDirectory}; filesForCommit.clear(); try { SVNCommitInfo info = ourClientManager.getCommitClient().doCommit(forCommit, false, comment, null, null, false, false, SVNDepth.INFINITY); Log.logDebug(LOGGER, "SVN committed into new revision {0}", info.getNewRevision()); if (info.getNewRevision() < 0) { // empty commit - file was not changed info = new SVNCommitInfo(Long.parseLong(getFileVersion("")), null, null, null); } Log.logInfoRB("SVN_FINISH", "commit"); return Long.toString(info.getNewRevision()); } catch (SVNException ex) { if (Arrays.asList(SVNErrorCode.FS_TXN_OUT_OF_DATE, SVNErrorCode.WC_NOT_UP_TO_DATE, SVNErrorCode.FS_CONFLICT) .contains(ex.getErrorMessage().getErrorCode())) { // Somebody else committed changes - it's normal. Will upload on next save. Log.logWarningRB("SVN_CONFLICT"); ourClientManager.getWCClient().doRevert(new File[] { baseDirectory }, SVNDepth.fromRecurse(true), null); return null; } else { Log.logErrorRB("SVN_ERROR", "commit", ex.getMessage()); checkNetworkException(ex); } throw ex; } catch (Exception ex) { Log.logErrorRB("SVN_ERROR", "commit", ex.getMessage()); throw ex; } } void checkNetworkException(Exception ex) throws NetworkException { if (ex.getCause() instanceof SocketException) { throw new NetworkException(ex.getCause()); } if (ex instanceof SVNException) { SVNException se = (SVNException) ex; if (se.getErrorMessage().getErrorCode().getCategory() == SVNErrorCode.RA_DAV_CATEGORY) { throw new NetworkException(se); } } } /** * Determines whether or not the supplied URL represents a valid Subversion repository. * * <p> * Does the equivalent of <code>svn info <i>url</i></code>. * * @param url * URL of supposed remote repository * @return true if repository appears to be valid, false otherwise */ public static boolean isSVNRepository(String url) { // Heuristics to save some waiting time try { ISVNOptions options = SVNWCUtil.createDefaultOptions(true); SVNClientManager ourClientManager = SVNClientManager.newInstance(options, (ISVNAuthenticationManager)null); ourClientManager.getWCClient().doInfo(SVNURL.parseURIEncoded(SVNEncodingUtil.autoURIEncode(url)), SVNRevision.HEAD, SVNRevision.HEAD); } catch (SVNAuthenticationException ex) { // TODO: Non-SVN URLs such as below give a false positive with this heuristic: // https://twitter.com/amadlonkay/status/699716236372889600 return true; } catch (SVNException ex) { return false; } return true; } }