package ebuild.repo.svn; import java.io.File; import java.io.FileFilter; import java.util.Date; 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.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.internal.wc.DefaultSVNOptions; 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 ebuild.api.common.IElementKey; import ebuild.api.common.IVersion; import ebuild.api.repo.AbstractSourceRepository; import ebuild.api.repo.RepositoryPluginException; import ebuild.util.FileUtil; public class SvnRepository extends AbstractSourceRepository{ final public SVNURL rootUrl; final public SVNClientManager clientManager; public SvnRepository(String rootUrl, Login login) throws SVNException{ this(SVNURL.parseURIEncoded(rootUrl),login); } public SvnRepository(SVNURL rootUrl, Login login) throws SVNException{ String protocol = rootUrl.getProtocol(); if("svn".equals(protocol)){ SVNRepositoryFactoryImpl.setup(); }else if("http".equals(protocol) || "https".equals(protocol)){ DAVRepositoryFactory.setup(); }else if("file".equals(protocol)){ FSRepositoryFactory.setup(); } this.rootUrl = rootUrl; DefaultSVNOptions options = SVNWCUtil.createDefaultOptions(true); clientManager = (login!=null) ?SVNClientManager.newInstance(options, login.username, login.password) :SVNClientManager.newInstance(options); } @Override public FileFilter getFileFilter() { return new FileFilter(){ public boolean accept(File f) { return !".svn".equals(f.getName()); } }; } public String getUri() { return rootUrl.toString(); } public String getUniverse() { return "ebuild"; } public String getLastChangeRevision(IElementKey key) throws RepositoryPluginException { String path = svnPath(key); try{ SVNURL url = svnUrl(path); SVNRevision revision = svnRevision(key.getVersion(),path); SVNInfo info = clientManager.getWCClient().doInfo(url, revision, revision); SVNRevision lcrevision = info.getCommittedRevision(); return lcrevision.getNumber()+""; }catch(SVNException e){ throw handleGenericSvnErrors(e, path); } } private SVNRevision doGetHeadRevision(SVNURL url) throws RepositoryPluginException{ try { SVNInfo info = clientManager.getWCClient().doInfo(url, SVNRevision.HEAD, SVNRevision.HEAD); return info.getRevision(); } catch (SVNException e) { throw new RepositoryPluginException(e.getMessage()); } } protected String doGetHeadRevision() throws RepositoryPluginException { return doGetHeadRevision(rootUrl).toString(); } public void fetchMeta(File metaDir, IElementKey key) throws RepositoryPluginException { String path = svnPath(key)+"/meta"; SVNRevision revision = svnRevision(key.getVersion(),path); export(metaDir, path, revision); Date timestamp = fetchCommittedDate(path, revision); FileUtil.writeTimestampFile(metaDir, timestamp); } @Override public void fetchProject(File projectDir, IElementKey key) throws RepositoryPluginException { String path = svnPath(key); SVNRevision revision = svnRevision(key.getVersion(),path); export(projectDir, path, revision); } private Date fetchCommittedDate(String path, SVNRevision revision) throws RepositoryPluginException{ try{ SVNURL url = svnUrl(path); SVNInfo info = clientManager.getWCClient().doInfo(url, revision, revision); return info.getCommittedDate(); }catch(SVNException e){ throw handleGenericSvnErrors(e, path); } } private long export(File dir, String path, SVNRevision revision) throws RepositoryPluginException{ if(revision==SVNRevision.HEAD) throw new Error("Should not be exporting head revision!"); try{ SVNURL url = svnUrl(path); return clientManager.getUpdateClient().doExport(url, dir, revision, revision, "\n", true, SVNDepth.INFINITY); }catch(SVNException e){ throw handleGenericSvnErrors(e, path); } } public String toString() { return "svn / "+rootUrl; } ////////// // UTILITY private RepositoryPluginException handleGenericSvnErrors(SVNException e, String path) { SVNErrorCode ecode = e.getErrorMessage().getErrorCode(); if(ecode==SVNErrorCode.RA_DAV_REQUEST_FAILED){ // FEATURE work if already checked out (although here it would be necessary to make sure any // versions are correct, which may be problematic when revision==HEAD return new RepositoryPluginException("Unable to connect to repository: "+rootUrl); }else if(ecode==SVNErrorCode.RA_ILLEGAL_URL){ return new RepositoryPluginException("Unknown path in subversion: "+path+" @ "+getUri()); }else{ return new RepositoryPluginException(e); } } private SVNRevision svnRevision(IVersion v, String path) throws RepositoryPluginException{ if(v.isHead()){ return SVNRevision.HEAD; }else if(!v.isTag()){ return SVNRevision.parse(v.getRevision()); }else{ // FEATURE - warn if version exists with more than one revision? SVNURL url = svnUrl(path); return doGetHeadRevision(url); // return SVNRevision.HEAD; } } private String svnPath(IElementKey key){ String projName = key.getOrg()+"-"+key.getName(); IVersion v = key.getVersion(); if(v.isTag()){ return "tags/"+v.getTag()+"/"+projName; }else{ String branch = v.getBranch(); if("trunk".equals(branch)){ return "trunk/"+projName; }else{ return "branches/"+v.getBranch()+"/"+projName; } } } private SVNURL svnUrl(String path){ try { return rootUrl.appendPath(path,false); } catch (SVNException e) { throw new Error(e); } } // @Override public ElementNature getElementNature() { return ElementNature.source; } // @Override public ElementType determineType(EKey key) throws ConfigurationException { // for(ElementType type: ElementType.values()){ // String path = svnPath(key)+"/meta/"+type.FILENAME; // SVNURL url = svnUrl(path); // SVNRevision revision = svnRevision(key.version); // try{ // clientManager.getWCClient().doInfo(url, revision, revision); // return type; // }catch(SVNException e){ // SVNErrorCode ecode = e.getErrorMessage().getErrorCode(); // if(ecode==SVNErrorCode.RA_ILLEGAL_URL){ // continue; // } // throw handleGenericSvnErrors(e, path); // } // } // return null; // } // // // // // // public SourceCheckout.Report fetchSource(File dir4checkout, EKey key) throws ConfigurationException { // // } // // // public void checkout(File dir4checkout, String path, SVNRevision revision) throws ConfigurationException { // try{ // long r0 = -1; // if(dir4checkout.exists()){ // r0 = getRevision(dir4checkout); // } // // if(revision == SVNRevision.HEAD){ // long r1 = doCheckout(dir4checkout, path, revision); // // String location = path +" @ "+rootUrl; // String message = " -> " + r1 + " "+location; // if(r0==-1){ // message = "checked out"+message; // return checkOutResponse(message, false, dir4checkout, r1); // }else if(r0r0, dir4checkout, r1); // }else{ // message = "upto date"+message; // return checkOutResponse(message, false, dir4checkout, r1); // } // }else{ // // long r1 = r0; // if(r0==-1){ // r1 = doCheckout(dir4checkout, path, revision); // } // // String location = path +" @ "+rootUrl; // String message = " -> " + r1 + " "+location; // if(r0==-1){ // message = "checked out"+message; // return checkOutResponse(message, false, dir4checkout, r1); // } // message = "upto date"+message; // return checkOutResponse(message, false, dir4checkout, r1); // } // } catch (SVNException e) { // throw newBroken(e); // } // } // // public long doCheckout(File dir4checkout, String path, SVNRevision revision) throws ConfigurationException { // try{ // SVNURL url = svnUrl(path); // SVNUpdateClient updateClient = clientManager.getUpdateClient(); // updateClient.setIgnoreExternals(false); // return updateClient.doCheckout(url, dir4checkout, revision, revision, SVNDepth.INFINITY, true); // }catch(SVNException e){ // SVNErrorCode ecode = e.getErrorMessage().getErrorCode(); // if(ecode==SVNErrorCode.WC_OBSTRUCTED_UPDATE){ // throw newProblem("A module with the same name has already been checked out from another repository (!) : "+path); // } // throw handleGenericSvnErrors(e, path); // } // } // // // private SourceCheckout.Report checkOutResponse(String msg, boolean dirty, File dir, long revision ){ // SvnWorkingDir r = new SvnWorkingDir(this, dir, revision, dirty); // return new SourceCheckout.Report(r, msg, dir); // } // // // public long getRevision(File workingDir) throws SVNException { // final SVNStatus status = clientManager.getStatusClient().doStatus(workingDir, false); // status.getURL(); // return status != null ? status.getRevision().getNumber() : -1; // } // // // // private long export(String path, File f, SVNRevision revision) throws ConfigurationException{ // try{ // SVNURL url = svnUrl(path); // return clientManager.getUpdateClient().doExport(url, f, revision, revision, "\n", true, SVNDepth.EMPTY); // }catch(SVNException e){ // throw handleGenericSvnErrors(e, path); // } // } // // private long exportMeta(File dir, EKey key, ElementType type) throws ConfigurationException { // SVNRevision revision = svnRevision(key); // String pathA = svnPath(key,type); // File fA = new File(dir, type.FILENAME); // long r1A = export(pathA, fA, revision); // // long r1B = -1; // String build = type.FILENAME_BUILD; // if(build!=null){ // String pathB = svnPath(key)+"/"+type.FILENAME_BUILD; // File fB = new File(dir, type.FILENAME_BUILD); // r1B = export(pathB, fB, revision); // } // long r = Math.max(r1A, r1B); // writeExportRevision(dir, r); // return r; // } // // // public RepositoryCheckout.Report fetchMeta(File dir, EKey key, ElementType type) throws ConfigurationException { // String path = svnPath(key); // String location = path +" @ "+rootUrl; // long r0 = readExportRevision(dir); // if(key.isHead()){ // long r1 = exportMeta(dir, key, type); // // // String message = " -> " + r1 + " "+location; // if(r0==-1){ // message = "meta exported "+message; // return checkOutResponse(message, false, dir, r1); // }else if(r0