001: package org.drools.scm.svn;
002:
003: import java.io.ByteArrayInputStream;
004: import java.io.ByteArrayOutputStream;
005: import java.io.IOException;
006: import java.io.InputStream;
007: import java.io.OutputStream;
008: import java.util.ArrayList;
009: import java.util.Collection;
010: import java.util.Collections;
011: import java.util.HashMap;
012: import java.util.Iterator;
013: import java.util.List;
014: import java.util.Map;
015: import java.util.Set;
016: import java.util.Map.Entry;
017:
018: //import org.apache.log4j.Logger;
019: import org.drools.scm.DefaultScmEntry;
020: import org.drools.scm.ScmAction;
021: import org.drools.scm.ScmActionFactory;
022: import org.drools.scm.ScmEntry;
023:
024: import org.drools.scm.log.ScmLogEntry;
025: import org.drools.scm.log.ScmLogEntry.Add;
026: import org.drools.scm.log.ScmLogEntry.Copy;
027: import org.drools.scm.log.ScmLogEntry.Delete;
028: import org.drools.scm.log.ScmLogEntry.Replaced;
029: import org.drools.scm.log.ScmLogEntry.Update;
030:
031: import org.tmatesoft.svn.core.SVNCommitInfo;
032: import org.tmatesoft.svn.core.SVNDirEntry;
033: import org.tmatesoft.svn.core.SVNErrorCode;
034: import org.tmatesoft.svn.core.SVNErrorMessage;
035: import org.tmatesoft.svn.core.SVNException;
036: import org.tmatesoft.svn.core.SVNLogEntry;
037: import org.tmatesoft.svn.core.SVNLogEntryPath;
038: import org.tmatesoft.svn.core.SVNNodeKind;
039: import org.tmatesoft.svn.core.SVNURL;
040: import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
041: import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
042: import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
043: import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
044: import org.tmatesoft.svn.core.io.ISVNEditor;
045: import org.tmatesoft.svn.core.io.ISVNWorkspaceMediator;
046: import org.tmatesoft.svn.core.io.SVNRepository;
047: import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
048: import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
049: import org.tmatesoft.svn.core.wc.SVNWCUtil;
050:
051: public class SvnActionFactory implements ScmActionFactory {
052:
053: // private static Logger logger = Logger.getLogger( SvnActionFactory.class );
054: private SVNRepository repository;
055:
056: /*
057: * Initializes the library to work with a repository either via svn:// (and
058: * svn+ssh://) or via http:// (and https://)
059: */
060: static {
061: // for DAV (over http and https)
062: DAVRepositoryFactory.setup();
063:
064: // for SVN (over svn and svn+ssh)
065: SVNRepositoryFactoryImpl.setup();
066:
067: // For File
068: FSRepositoryFactory.setup();
069: }
070:
071: public SVNRepository getSVNRepository() {
072: return this .repository;
073: }
074:
075: public ScmAction addDirectory(String root, String path) {
076: return new AddDirectory(root, path);
077: }
078:
079: public ScmAction addFile(String path, String file, byte[] content) {
080: return new AddFile(path, file, content);
081: }
082:
083: public ScmAction copyDirectory(String path, String newPath,
084: long revision) {
085: return new CopyDirectory(path, newPath, revision);
086: }
087:
088: public ScmAction copyFile(String path, String file, String newPath,
089: String newFile, long revision) {
090: return new CopyFile(path, file, newPath, newFile, revision);
091: }
092:
093: public ScmAction deleteDirectory(String path) {
094: return new DeleteDirectory(path);
095: }
096:
097: public ScmAction deleteFile(String path, String file) {
098: return new DeleteFile(path, file);
099: }
100:
101: public ScmAction moveDirectory(String path, String newPath,
102: long revision) {
103: return new MoveDirectory(path, newPath, revision);
104: }
105:
106: public ScmAction moveFile(String path, String file, String newPath,
107: String newFile, long revision) {
108: return new MoveFile(path, file, newPath, newFile, revision);
109: }
110:
111: public ScmAction updateFile(String path, String file,
112: byte[] oldContent, byte[] newContent) {
113: return new UpdateFile(path, file, oldContent, newContent);
114: }
115:
116: public SvnActionFactory(String url, String svnUsername,
117: String svnPassword) throws Exception {
118: String username = svnUsername;
119: String password = svnPassword;
120: try {
121: this .repository = SVNRepositoryFactory.create(SVNURL
122: .parseURIEncoded(url));
123: ISVNAuthenticationManager authManager = SVNWCUtil
124: .createDefaultAuthenticationManager(username,
125: password);
126: repository.setAuthenticationManager(authManager);
127:
128: SVNNodeKind nodeKind = repository.checkPath("", -1);
129: if (nodeKind == SVNNodeKind.NONE) {
130: SVNErrorMessage err = SVNErrorMessage.create(
131: SVNErrorCode.UNKNOWN,
132: "No entry at URL ''{0}''", url);
133: throw new SVNException(err);
134: } else if (nodeKind == SVNNodeKind.FILE) {
135: SVNErrorMessage err = SVNErrorMessage
136: .create(
137: SVNErrorCode.UNKNOWN,
138: "Entry at URL ''{0}'' is a file while directory was expected",
139: url);
140: throw new SVNException(err);
141: }
142: } catch (SVNException e) {
143: e.printStackTrace();
144: //logger.error( "svn error: " );
145: throw e;
146: }
147: }
148:
149: public void getContent(String path, String file, long revision,
150: OutputStream os) throws SVNException {
151: this .repository.getFile(path + "/" + file, revision, null, os);
152: }
153:
154: public List log(String[] paths, long startRevision, long endRevision)
155: throws SVNException {
156: return toScm(this .repository.log(paths, null, startRevision,
157: endRevision, true, false));
158: }
159:
160: private List toScm(Collection collection) throws SVNException {
161: List list = new ArrayList();
162: for (Iterator it = collection.iterator(); it.hasNext();) {
163: SVNLogEntry logEntry = (SVNLogEntry) it.next();
164: Map map = logEntry.getChangedPaths();
165: Set changePathSet = map.keySet();
166:
167: ScmLogEntry scmLogEntry = new ScmLogEntry(logEntry
168: .getAuthor(), logEntry.getDate(), logEntry
169: .getMessage());
170: for (Iterator it2 = changePathSet.iterator(); it2.hasNext();) {
171: SVNLogEntryPath entryPath = (SVNLogEntryPath) map
172: .get(it2.next());
173:
174: switch (entryPath.getType()) {
175:
176: case SVNLogEntryPath.TYPE_ADDED: {
177: SVNDirEntry dirEntry = this .repository.info(
178: entryPath.getPath(), -1);
179: char type = (dirEntry.getKind() == SVNNodeKind.DIR) ? 'D'
180: : 'F';
181: if (entryPath.getCopyPath() == null) {
182: // this entry was added
183: Add add = new Add(type, entryPath.getPath(),
184: logEntry.getRevision());
185: scmLogEntry.addAction(add);
186: break;
187: } else {
188: // this entry was copied
189: Copy copy = new Copy(type, entryPath
190: .getCopyPath(), entryPath
191: .getCopyRevision(),
192: entryPath.getPath(), logEntry
193: .getRevision());
194: scmLogEntry.addAction(copy);
195: break;
196: }
197: }
198:
199: case SVNLogEntryPath.TYPE_DELETED: {
200: SVNDirEntry dirEntry = this .repository.info(
201: entryPath.getPath(), -1);
202: char type = (dirEntry.getKind() == SVNNodeKind.DIR) ? 'D'
203: : 'F';
204: Delete delete = new Delete(type, entryPath
205: .getPath(), logEntry.getRevision());
206: scmLogEntry.addAction(delete);
207: break;
208: }
209:
210: case SVNLogEntryPath.TYPE_MODIFIED: {
211: SVNDirEntry dirEntry = this .repository.info(
212: entryPath.getPath(), -1);
213: char type = (dirEntry.getKind() == SVNNodeKind.DIR) ? 'D'
214: : 'F';
215: Update update = new Update(type, entryPath
216: .getPath(), logEntry.getRevision());
217: scmLogEntry.addAction(update);
218: break;
219: }
220:
221: case SVNLogEntryPath.TYPE_REPLACED: {
222: SVNDirEntry dirEntry = this .repository.info(
223: entryPath.getPath(), -1);
224: char type = (dirEntry.getKind() == SVNNodeKind.DIR) ? 'D'
225: : 'F';
226: Replaced replaced = new Replaced(type, entryPath
227: .getPath(), logEntry.getRevision());
228: scmLogEntry.addAction(replaced);
229: break;
230: }
231: }
232: }
233: list.add(scmLogEntry);
234: }
235: return list;
236: }
237:
238: public long getLatestRevision() throws Exception {
239: try {
240: return repository.getLatestRevision();
241: } catch (SVNException e) {
242: e.printStackTrace();
243: //logger.error( "svn error: " );
244: throw e;
245: }
246: }
247:
248: public void execute(ScmAction action, String message)
249: throws Exception {
250: try {
251: ISVNEditor editor = this .repository.getCommitEditor(
252: message, null);
253: editor.openRoot(-1);
254: action.applyAction(editor);
255: editor.closeDir();
256:
257: SVNCommitInfo info = editor.closeEdit();
258: } catch (SVNException e) {
259: e.printStackTrace();
260: //logger.error( "svn error: " );
261: throw e;
262: }
263: }
264:
265: public static class AddFile implements ScmAction {
266: private String file;
267: private String path;
268: private byte[] content;
269:
270: public AddFile(String path, String file, byte[] content) {
271: this .path = path;
272: this .file = file;
273: this .content = content;
274: }
275:
276: public void applyAction(Object context) throws SVNException {
277: ISVNEditor editor = (ISVNEditor) context;
278: openDirectories(editor, path);
279:
280: editor.addFile(path + "/" + file, null, -1);
281: editor.applyTextDelta(path + "/" + file, null);
282: SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
283: String checksum = deltaGenerator.sendDelta(path + "/"
284: + file, new ByteArrayInputStream(this .content),
285: editor, true);
286: editor.closeFile(path + "/" + file, checksum);
287:
288: closeDirectories(editor, path);
289: }
290: }
291:
292: /**
293: * root should be the last, previously created, parent folder. Each directory in the path
294: * will be created.
295: *
296: */
297: public static class AddDirectory implements ScmAction {
298: private String root;
299: private String path;
300:
301: public AddDirectory(String root, String path) {
302: this .root = root;
303: this .path = path;
304: }
305:
306: public void applyAction(Object context) throws SVNException {
307: ISVNEditor editor = (ISVNEditor) context;
308: openDirectories(editor, this .root);
309: String[] paths = this .path.split("/");
310: String newPath = this .root;
311: for (int i = 0, length = paths.length; i < length; i++) {
312: newPath = (newPath.length() != 0) ? newPath + "/"
313: + paths[i] : paths[i];
314:
315: editor.addDir(newPath, null, -1);
316: }
317:
318: closeDirectories(editor, path);
319: closeDirectories(editor, this .root);
320: }
321: }
322:
323: public static class UpdateFile implements ScmAction {
324: private String file;
325: private String path;
326: private byte[] oldContent;
327: private byte[] newContent;
328:
329: public UpdateFile(String path, String file, byte[] oldContent,
330: byte[] newContent) {
331: this .path = path;
332: this .file = file;
333: this .oldContent = oldContent;
334: this .newContent = newContent;
335: }
336:
337: public void applyAction(Object context) throws SVNException {
338: ISVNEditor editor = (ISVNEditor) context;
339: openDirectories(editor, path);
340: editor.openFile(path + "/" + file, -1);
341:
342: editor.applyTextDelta(path + "/" + file, null);
343: SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
344: String checksum = deltaGenerator.sendDelta(path + "/"
345: + file, new ByteArrayInputStream(this .oldContent),
346: 0, new ByteArrayInputStream(this .newContent),
347: editor, true);
348: editor.closeFile(path + "/" + file, checksum);
349: closeDirectories(editor, path);
350: }
351: }
352:
353: public static class CopyFile implements ScmAction {
354: private String file;
355: private String path;
356: private String newPath;
357: private String newFile;
358: private long revision;
359:
360: public CopyFile(String path, String file, String newPath,
361: String newFile, long revision) {
362: this .path = path;
363: this .file = file;
364: this .newPath = newPath;
365: this .newFile = newFile;
366: this .revision = revision;
367: }
368:
369: public void applyAction(Object context) throws SVNException {
370: ISVNEditor editor = (ISVNEditor) context;
371: editor.addFile(newPath + "/" + newFile, path + "/" + file,
372: revision);
373: }
374: }
375:
376: public static class CopyDirectory implements ScmAction {
377: private String path;
378: private String newPath;
379: private long revision;
380:
381: public CopyDirectory(String path, String newPath, long revision) {
382: this .path = path;
383: this .newPath = newPath;
384: this .revision = revision;
385: }
386:
387: public void applyAction(Object context) throws SVNException {
388: ISVNEditor editor = (ISVNEditor) context;
389: editor.addDir(newPath, path, revision);
390: editor.closeDir();
391: }
392: }
393:
394: public static class MoveFile implements ScmAction {
395: private String file;
396: private String path;
397: private String newPath;
398: private String newFile;
399: private long revision;
400:
401: public MoveFile(String path, String file, String newPath,
402: String newFile, long revision) {
403: this .path = path;
404: this .file = file;
405: this .newPath = newPath;
406: this .newFile = newFile;
407: this .revision = revision;
408: }
409:
410: public void applyAction(Object context) throws SVNException {
411: ISVNEditor editor = (ISVNEditor) context;
412: CopyFile copyFile = new CopyFile(path, file, newPath,
413: newFile, revision);
414: DeleteFile deleteFile = new DeleteFile(path, file);
415:
416: copyFile.applyAction(editor);
417: deleteFile.applyAction(editor);
418:
419: }
420: }
421:
422: public static class MoveDirectory implements ScmAction {
423: private String path;
424: private String newPath;
425: private long revision;
426:
427: public MoveDirectory(String path, String newPath, long revision) {
428: this .path = path;
429: this .newPath = newPath;
430: this .revision = revision;
431: }
432:
433: public void applyAction(Object context) throws SVNException {
434: ISVNEditor editor = (ISVNEditor) context;
435: CopyDirectory copyDirectory = new CopyDirectory(path,
436: newPath, revision);
437: DeleteDirectory deleteDirectory = new DeleteDirectory(path);
438:
439: copyDirectory.applyAction(editor);
440: deleteDirectory.applyAction(editor);
441:
442: }
443: }
444:
445: public static class DeleteFile implements ScmAction {
446: private String path;
447: private String file;
448:
449: public DeleteFile(String path, String file) {
450: this .path = path;
451: this .file = file;
452: }
453:
454: public void applyAction(Object context) throws SVNException {
455: ISVNEditor editor = (ISVNEditor) context;
456: openDirectories(editor, path);
457: editor.deleteEntry(path + "/" + file, -1);
458: closeDirectories(editor, path);
459: }
460: }
461:
462: public static class DeleteDirectory implements ScmAction {
463: private String path;
464:
465: public DeleteDirectory(String path) {
466: this .path = path;
467: }
468:
469: public void applyAction(Object context) throws SVNException {
470: ISVNEditor editor = (ISVNEditor) context;
471: openDirectories(editor, path);
472: editor.deleteEntry(path, -1);
473: closeDirectories(editor, path);
474: }
475: }
476:
477: public static class CommitMediator implements ISVNWorkspaceMediator {
478: private Map myTmpStorages = new HashMap();
479:
480: /*
481: * This may be implemented to get properties from
482: * '.svn/wcprops'
483: */
484: public String getWorkspaceProperty(String path, String name)
485: throws SVNException {
486: return null;
487: }
488:
489: /*
490: * This may be implemented to set properties in
491: * '.svn/wcprops'
492: */
493: public void setWorkspaceProperty(String path, String name,
494: String value) throws SVNException {
495: }
496:
497: /*
498: * Creates a temporary file delta storage. id will be
499: * used as the temporary storage identifier. Returns an
500: * OutputStream to write the delta data into the temporary
501: * storage.
502: */
503: public OutputStream createTemporaryLocation(String path,
504: Object id) throws IOException {
505: ByteArrayOutputStream tempStorageOS = new ByteArrayOutputStream();
506: myTmpStorages.put(id, tempStorageOS);
507: return tempStorageOS;
508: }
509:
510: /*
511: * Returns an InputStream of the temporary file delta
512: * storage identified by id to read the delta.
513: */
514: public InputStream getTemporaryLocation(Object id)
515: throws IOException {
516: return new ByteArrayInputStream(
517: ((ByteArrayOutputStream) myTmpStorages.get(id))
518: .toByteArray());
519: }
520:
521: /*
522: * Gets the length of the delta that was written
523: * to the temporary storage identified by id.
524: */
525: public long getLength(Object id) throws IOException {
526: ByteArrayOutputStream tempStorageOS = (ByteArrayOutputStream) myTmpStorages
527: .get(id);
528: if (tempStorageOS != null) {
529: return tempStorageOS.size();
530: }
531: return 0;
532: }
533:
534: /*
535: * Deletes the temporary file delta storage identified
536: * by id.
537: */
538: public void deleteTemporaryLocation(Object id) {
539: myTmpStorages.remove(id);
540: }
541: }
542:
543: public static void openDirectories(ISVNEditor editor, String path)
544: throws SVNException {
545: int pos = path.indexOf('/', 0);
546: while (pos != -1) {
547: editor.openDir(path.substring(0, pos), -1);
548: pos = path.indexOf('/', pos + 1);
549: }
550: editor.openDir(path.substring(0, path.length()), -1);
551: }
552:
553: public static void closeDirectories(ISVNEditor editor, String path)
554: throws SVNException {
555: int length = path.length() - 1;
556: int pos = path.lastIndexOf('/', length);
557: editor.closeDir();
558: while (pos != -1) {
559: editor.closeDir();
560: pos = path.lastIndexOf('/', pos - 1);
561: }
562: }
563:
564: public List listEntries(String path) throws SVNException {
565: List entries = new ArrayList();
566: listEntries(path, entries);
567: return entries;
568: }
569:
570: public void listEntries(String path, List list) throws SVNException {
571: Collection entries = this .repository.getDir(path, -1, null,
572: (Collection) null);
573: Iterator iterator = entries.iterator();
574: while (iterator.hasNext()) {
575: SVNDirEntry svnEntry = (SVNDirEntry) iterator.next();
576:
577: DefaultScmEntry scmEntry = new DefaultScmEntry();
578: scmEntry
579: .setPath(path.equals("") ? path : path.substring(1));
580: scmEntry.setName(svnEntry.getName());
581: scmEntry.setAuthor(svnEntry.getAuthor());
582: scmEntry.setDate(svnEntry.getDate());
583: scmEntry.setRevision(svnEntry.getRevision());
584: scmEntry.setSize(svnEntry.getSize());
585: scmEntry
586: .setType((svnEntry.getKind() == SVNNodeKind.DIR) ? ScmEntry.DIRECTORY
587: : ScmEntry.FILE);
588: list.add(scmEntry);
589:
590: if (svnEntry.getKind() == SVNNodeKind.DIR) {
591: listEntries(path + "/" + svnEntry.getName(), list);
592: }
593: }
594: }
595:
596: }
|