1 /*** 2 * Copyright (C) 2008 Jeremy Thomerson (jthomerson@users.sourceforge.net) 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package net.sf.vcaperture.model; 18 19 import java.io.ByteArrayOutputStream; 20 import java.util.ArrayList; 21 import java.util.Collection; 22 import java.util.HashMap; 23 import java.util.Iterator; 24 import java.util.List; 25 26 import org.tmatesoft.svn.core.SVNException; 27 import org.tmatesoft.svn.core.SVNLogEntry; 28 import org.tmatesoft.svn.core.SVNLogEntryPath; 29 import org.tmatesoft.svn.core.SVNNodeKind; 30 import org.tmatesoft.svn.core.SVNURL; 31 import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; 32 import org.tmatesoft.svn.core.io.SVNRepositoryFactory; 33 34 /*** 35 * An implementation of <tt>AbstractRepository</tt> that allows access to a Subversion repository. 36 * Internally, this implementation uses SVNKit. 37 * 38 * @author Jeremy Thomerson (jthomerson@users.sourceforge.net) 39 * @see http://svnkit.com/ 40 * @see http://subversion.tigris.org/ 41 */ 42 public class SVNRepository extends AbstractRepository { 43 44 private boolean mInitialized; 45 private org.tmatesoft.svn.core.io.SVNRepository mRepo; 46 47 @Override 48 public boolean isRevisionNewer(String name, String startingRevision) { 49 return Long.parseLong(name) > Long.parseLong(startingRevision); 50 } 51 52 @Override 53 public String getLatestRevision() { 54 init(); 55 try { 56 return Long.toString(mRepo.getLatestRevision()); 57 } catch (SVNException se) { 58 throw new RuntimeException("Error getting latest revision: " + se.getMessage(), se); 59 } 60 } 61 62 @Override 63 public Collection<Revision> getRevisions(String startingRevision, int maxRevisions) { 64 init(); 65 List<Revision> list = new ArrayList<Revision>(); 66 Collection<SVNLogEntry> entries = new ArrayList<SVNLogEntry>(); 67 try { 68 long start = Long.parseLong(startingRevision); 69 mRepo.log(new String[] { "" }, entries, start, mRepo.getLatestRevision(), true, false); 70 LOGGER.debug("retrieved " + entries.size() + " entries"); 71 int cnt = 0; 72 for (SVNLogEntry entry : entries) { 73 if (cnt++ >= maxRevisions) { 74 break; 75 } 76 LOGGER.debug("retrieving entry: " + entry.getRevision()); 77 Revision rev = new Revision(); 78 rev.setName(Long.toString(entry.getRevision())); 79 rev.setCommitMessage(entry.getMessage()); 80 rev.setCommitDate(entry.getDate()); 81 rev.setAuthor(entry.getAuthor()); 82 83 List<RepoFileRevision> files = new ArrayList<RepoFileRevision>(); 84 for (Iterator it = entry.getChangedPaths().keySet().iterator(); it.hasNext();) { 85 SVNLogEntryPath entryPath = (SVNLogEntryPath) entry.getChangedPaths().get(it.next()); 86 RepoFileRevision rfr = new RepoFileRevision(); 87 rfr.setAuthor(rev.getAuthor()); 88 rfr.setRelativePath(entryPath.getPath()); 89 rfr.setRevision(rev.getName()); 90 SVNNodeKind kind = mRepo.checkPath(entryPath.getPath(), entry.getRevision()); 91 rfr.setDirectory(kind == SVNNodeKind.DIR); 92 rfr.setAction(mapAction(entryPath.getType())); 93 if (entryPath.getType() != SVNLogEntryPath.TYPE_DELETED && kind != SVNNodeKind.DIR) { 94 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 95 mRepo.getFile(entryPath.getPath(), entry.getRevision(), new HashMap(), baos); 96 rfr.setContents(baos.toString()); 97 } 98 if (entryPath.getCopyPath() != null) { 99 RepoFileRevision copy = new RepoFileRevision(); 100 copy.setRelativePath(entryPath.getCopyPath()); 101 copy.setRevision(Long.toString(entryPath.getCopyRevision())); 102 rfr.setCopyOfRevision(copy); 103 } 104 files.add(rfr); 105 } 106 rev.setFiles(files.toArray(new RepoFileRevision[files.size()])); 107 list.add(rev); 108 } 109 } catch (Exception ex) { 110 throw new RuntimeException("Error getting revisions: " + ex.getMessage(), ex); 111 } 112 return list; 113 } 114 115 private RepoFileAction mapAction(char type) { 116 switch (type) { 117 case SVNLogEntryPath.TYPE_MODIFIED: 118 return RepoFileAction.MODIFIED; 119 case SVNLogEntryPath.TYPE_ADDED: 120 return RepoFileAction.ADDED; 121 case SVNLogEntryPath.TYPE_DELETED: 122 return RepoFileAction.DELETED; 123 default: 124 break; 125 } 126 return null; 127 } 128 129 private synchronized void init() { 130 if (!isInitialized()) { 131 DAVRepositoryFactory.setup(); 132 try { 133 SVNURL url = SVNURL.parseURIDecoded(getURL()); 134 mRepo = SVNRepositoryFactory.create(url); 135 LOGGER.debug("----------------------------------------------------------------------------------------------------"); 136 LOGGER.debug("Connected to SVN repository: " + getURL()); 137 LOGGER.debug("Repository Root: " + mRepo.getRepositoryRoot(true)); 138 LOGGER.debug("Repository UUID: " + mRepo.getRepositoryUUID(true)); 139 LOGGER.debug("Latest Revision: " + mRepo.getLatestRevision()); 140 LOGGER.debug("----------------------------------------------------------------------------------------------------"); 141 setInitialized(true); 142 } catch (Exception ex) { 143 throw new RuntimeException("Unable to create backing repository: " + ex.getMessage(), ex); 144 } 145 } 146 } 147 148 public boolean isInitialized() { 149 return mInitialized; 150 } 151 152 public void setInitialized(boolean initialized) { 153 mInitialized = initialized; 154 } 155 156 @Override 157 public RepoFile getFile(String relativePath) { 158 throw new UnsupportedOperationException("Need to implement if this method is necessary."); 159 } 160 }