package net.oni2.svnaccess;
import static java.lang.System.err;
import java.io.File;
import java.util.Vector;
import net.oni2.ProxySettings;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.BasicAuthenticationManager;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
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.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
/**
* SVN handling
*
* @author Christian Illy
*/
public class SVN {
SVNClientManager svnCManager = null;
private void setup() {
// For using over http:// and https://
DAVRepositoryFactory.setup();
// For using over svn:// and svn+xxx://
SVNRepositoryFactoryImpl.setup();
// For using over file:///
FSRepositoryFactory.setup();
}
private void setProxy(BasicAuthenticationManager authMan) {
ProxySettings prox = ProxySettings.getInstance();
if (prox.validate() && prox.isUseProxy()) {
authMan.setProxy(prox.getHostOrIp(), prox.getPort(), null, null);
}
}
private BasicAuthenticationManager getAuthManager() {
BasicAuthenticationManager auth = new BasicAuthenticationManager(
new SVNAuthentication[0]);
setProxy(auth);
return auth;
}
private BasicAuthenticationManager getAuthManager(String username,
String password) {
BasicAuthenticationManager auth = new BasicAuthenticationManager(
username, password);
setProxy(auth);
return auth;
}
/**
* Constructor
*/
public SVN() {
setup();
svnCManager = SVNClientManager.newInstance(
SVNWCUtil.createDefaultOptions(true), getAuthManager());
}
/**
* Constructor with init values
*
* @param username
* Username
* @param password
* Password
*/
public SVN(String username, String password) {
setup();
svnCManager = SVNClientManager.newInstance(
SVNWCUtil.createDefaultOptions(true),
getAuthManager(username, password));
}
/**
* Checkout/update a repository to a local path
*
* @param reposUrl
* Repository URL
* @param wcDir
* Local path
* @param listener
* The listener for the status events
* @return True if successful
* @throws Exception
* if missing parameters or something went wrong
*/
public boolean updateWC(String reposUrl, File wcDir,
SVNUpdateListener listener) throws Exception {
SVNURL repos = SVNURL.parseURIEncoded(reposUrl);
if (wcDir.exists()) {
int rev = pathIsWCof(repos, wcDir);
if (rev < 0)
throw new Exception(
"Destination path exists but is not a Working Copy of the SVN");
return update(repos, wcDir, rev, listener);
} else {
return checkout(repos, wcDir, listener);
}
}
/**
* Checks if the SVN contains newer revisions than the local working copy
*
* @param reposUrl
* URL of repository to check for newer revisions
* @param wcDir
* Local working copy path to compare against
* @return -2: No connection to remote repos
* -1: No local working copy yet
* 0: Revisions are equal
* 1: SVN contains newer revisions
* 2: WC has manually deleted files
* @throws Exception
* If destination is not a WC of the given repository
*/
public int checkSVN(String reposUrl, File wcDir) throws Exception {
SVNURL repos = SVNURL.parseURIEncoded(reposUrl);
if (wcDir.exists()) {
int localRev = pathIsWCof(repos, wcDir);
if (localRev < 0) {
if (wcDir.listFiles().length > 0) {
throw new Exception(
"Destination path exists but is not a Working Copy of the SVN");
} else {
wcDir.delete();
return -1;
}
}
int remoteRev = getRemoteHeadRevision(repos);
if (remoteRev > localRev)
return 1;
else {
if (remoteRev < 0) {
return -2;
} else {
if (getMissingFiles(wcDir))
return 2;
else
return 0;
}
}
} else {
return -1;
}
}
private boolean getMissingFiles(File wcDir) {
try {
final Vector files = new Vector();
svnCManager.getStatusClient().doStatus(wcDir, null,
SVNDepth.INFINITY, false, false, false, false,
new ISVNStatusHandler() {
@Override
public void handleStatus(SVNStatus status)
throws SVNException {
SVNStatusType stat = status
.getCombinedNodeAndContentsStatus();
if (stat == SVNStatusType.MISSING
|| stat == SVNStatusType.STATUS_MISSING) {
files.add(status.getFile().getPath());
}
}
}, null);
return files.size() > 0;
} catch (SVNException e) {
e.printStackTrace();
}
return false;
}
private int getRemoteHeadRevision(SVNURL reposUrl) {
try {
SVNInfo info = svnCManager.getWCClient().doInfo(reposUrl,
SVNRevision.HEAD, SVNRevision.HEAD);
return (int) info.getRevision().getNumber();
} catch (SVNException e) {
e.printStackTrace();
}
return -1;
}
private int pathIsWCof(SVNURL reposUrl, File wcDir) {
if (wcDir.exists()) {
try {
SVNInfo info = svnCManager.getWCClient().doInfo(wcDir,
SVNRevision.WORKING);
if (info.getURL().equals(reposUrl))
return (int) info.getRevision().getNumber();
} catch (SVNException e) {
err.println("Error while getting information of working copy for the location '"
+ reposUrl + "': " + e.getMessage());
e.printStackTrace();
}
}
return -1;
}
private Vector getUpdatedFilesInRepository(SVNURL reposUrl,
int fromRev) {
Vector list = new Vector();
try {
svnCManager.getLogClient().doLog(reposUrl,
new String[] { reposUrl.getPath() }, SVNRevision.HEAD,
SVNRevision.create(fromRev + 1), SVNRevision.HEAD, true,
true, 0, new LogEntryHandler(list, reposUrl.getPath()));
} catch (Exception e) {
if (!e.getMessage().contains("No such revision ")) {
err.println("Error while getting the list of updated files of the location '"
+ reposUrl + "': " + e.getMessage());
e.printStackTrace();
}
}
return list;
}
private boolean update(SVNURL reposUrl, File wcDir, int fromRev,
SVNUpdateListener listener) throws Exception {
Vector updatedFiles = getUpdatedFilesInRepository(reposUrl,
fromRev);
svnCManager.getUpdateClient().setEventHandler(
new UpdateEventHandler(updatedFiles, listener));
try {
svnCManager.getUpdateClient().doUpdate(wcDir, SVNRevision.HEAD,
SVNDepth.INFINITY, true, true);
return true;
} catch (Exception e) {
err.println("Error while updating the working copy for the location '"
+ reposUrl + "': " + e.getMessage());
e.printStackTrace();
}
return false;
}
private Vector getFilesInRepository(SVNURL reposUrl)
throws Exception {
Vector list = new Vector();
try {
svnCManager.getLogClient().doList(reposUrl, SVNRevision.HEAD,
SVNRevision.HEAD, false, SVNDepth.INFINITY,
SVNDirEntry.DIRENT_ALL, new DirEntryHandler(list));
} catch (Exception e) {
err.println("Error while getting the list of files of the location '"
+ reposUrl + "': " + e.getMessage());
e.printStackTrace();
}
return list;
}
private boolean checkout(SVNURL reposUrl, File wcDir,
SVNUpdateListener listener) throws Exception {
Vector newFiles = getFilesInRepository(reposUrl);
svnCManager.getUpdateClient().setEventHandler(
new UpdateEventHandler(newFiles, listener));
boolean result = false;
try {
wcDir.mkdirs();
svnCManager.getUpdateClient()
.doCheckout(reposUrl, wcDir, SVNRevision.HEAD,
SVNRevision.HEAD, SVNDepth.INFINITY, true);
result = true;
} catch (Exception e) {
err.println("Error while checking out a working copy for the location '"
+ reposUrl + "': " + e.getMessage());
e.printStackTrace();
}
return result;
}
}