001 package org.hackystat.sensor.ant.svn; 002 003 import java.util.Collection; 004 import java.util.Date; 005 006 import org.tmatesoft.svn.core.SVNLogEntry; 007 import org.tmatesoft.svn.core.SVNURL; 008 import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; 009 import org.tmatesoft.svn.core.internal.io.dav.http.DefaultHTTPConnectionFactory; 010 import org.tmatesoft.svn.core.internal.io.dav.http.IHTTPConnectionFactory; 011 import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl; 012 import org.tmatesoft.svn.core.io.SVNRepository; 013 import org.tmatesoft.svn.core.io.SVNRepositoryFactory; 014 import org.tmatesoft.svn.core.wc.SVNWCUtil; 015 016 /** 017 * SVN Repository processor to extract commit information. 018 * 019 * @author (Cedric) Qin ZHANG 020 * @version $Id$ 021 */ 022 public class SVNCommitProcessor { 023 private SVNRepository svnRepository; 024 025 /** 026 * Constructs this instance. 027 * 028 * @param repositoryUrl The svn repository url. It can points to a 029 * subdirectory in the repository. 030 * @param userName The user name. Null is a valid value. If either user name 031 * or password is null, then anonymous credential is used to access the svn 032 * repository. 033 * @param password The password. Null is a valid value. 034 * 035 * @throws Exception If there is any error. 036 */ 037 public SVNCommitProcessor(String repositoryUrl, String userName, String password) 038 throws Exception { 039 if (repositoryUrl.startsWith("http://") || repositoryUrl.startsWith("https://")) { 040 IHTTPConnectionFactory factory = 041 new DefaultHTTPConnectionFactory(null, true, null); 042 DAVRepositoryFactory.setup(factory); 043 } 044 else if (repositoryUrl.startsWith("svn://")) { 045 SVNRepositoryFactoryImpl.setup(); 046 } 047 else { 048 throw new Exception("Repository url must start with http|https|svn."); 049 } 050 051 this.svnRepository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(repositoryUrl)); 052 if (userName != null && password != null) { 053 this.svnRepository.setAuthenticationManager(SVNWCUtil 054 .createDefaultAuthenticationManager(userName, password)); 055 } 056 } 057 058 /** 059 * Gets the largest revision number at the date specified. 060 * 061 * @param date The date. 062 * 063 * @return The revision number. 064 * 065 * @throws Exception If there is any error. 066 */ 067 public long getRevisionNumber(Date date) throws Exception { 068 // If date is too large to too small, such as new Date(Long.MIN_VALUE) 069 // or new Date(Long.MAX_VALUE), then the underlying library JavaSNV 070 // SVNRepository.getDatedRevision() call 071 // will either raise exception or give an erronous version number. 072 // We try to avoid the error here. 073 074 long startLogTime = Long.MAX_VALUE; 075 long endLogTime = Long.MIN_VALUE; 076 SVNLogEntry startLog = null; 077 SVNLogEntry endLog = null; 078 079 long latestRevision = this.svnRepository.getLatestRevision(); 080 Collection<?> svnLogEntries = this.svnRepository.log(new String[] { "" }, null, 1, 081 latestRevision, true, true); 082 for (Object entry : svnLogEntries) { 083 SVNLogEntry log = (SVNLogEntry) entry; 084 long time = log.getDate().getTime(); 085 if (time < startLogTime) { 086 startLogTime = time; 087 startLog = log; 088 } 089 if (time > endLogTime) { 090 endLogTime = time; 091 endLog = log; 092 } 093 } 094 095 if (startLog == null && endLog == null) { 096 // we cannot determine revision time bounds, give JavaSVN a shot 097 return this.svnRepository.getDatedRevision(date); 098 } 099 else { 100 long targetTime = date.getTime(); 101 if (targetTime < startLogTime) { 102 return 0; 103 } 104 else if (endLogTime < targetTime) { 105 return latestRevision; 106 } 107 else { 108 return this.svnRepository.getDatedRevision(date); 109 } 110 } 111 } 112 113 /** 114 * Gets commit record for a specified revision. Note that if the repository 115 * url supplied in the constructor points to a subdirectory of the repository 116 * root or a file, then there might not be any commits for the revision. In 117 * this case, null is returned. 118 * 119 * @param revision The revision number. 120 * 121 * @return The commit record or null. 122 * 123 * @throws Exception If there is any error. 124 */ 125 public CommitRecord getCommitRecord(long revision) throws Exception { 126 Collection<?> svnLogEntries = this.svnRepository.log(new String[] { "" }, null, revision, 127 revision, true, true); 128 129 // since startRevision and endRevision are the same, we should have at most 130 // 1 svn log entry. 131 if (svnLogEntries == null || svnLogEntries.isEmpty()) { 132 return null; 133 } 134 else if (svnLogEntries.size() > 1) { 135 throw new RuntimeException("Assertion failure. JavaSVN client error?"); 136 } 137 else { // exactly 1 svn log entry 138 SVNLogEntry logEntry = (SVNLogEntry) svnLogEntries.iterator().next(); 139 return new CommitRecord(this.svnRepository, logEntry); 140 } 141 } 142 }