001 /*
002 * Sandbox.java
003 * Copyright (c) 1998-2008, The University of Sheffield.
004 *
005 * This code is from the GATE project (http://gate.ac.uk/) and is free
006 * software licenced under the GNU General Public License version 3. It is
007 * distributed without any warranty. For more details see COPYING.txt in the
008 * top level directory (or at http://gatewiki.sf.net/COPYING.txt).
009 *
010 * Hamish Cunningham 8th January 2007
011 *
012 * Based on example code from SVNKit in the
013 * org.tmatesoft.svn.examples.wc
014 * package. Thanks TMate Software Ltd.
015 */
016 package gate.versioning.svnkit;
017
018 import java.io.File;
019 import java.util.concurrent.locks.ReentrantLock;
020
021 import org.apache.log4j.Logger;
022 import org.tmatesoft.svn.core.SVNCommitInfo;
023 import org.tmatesoft.svn.core.SVNDepth;
024 import org.tmatesoft.svn.core.SVNException;
025 import org.tmatesoft.svn.core.SVNCancelException;
026 import org.tmatesoft.svn.core.SVNURL;
027 import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
028 import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
029 import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
030 import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
031 import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
032 import org.tmatesoft.svn.core.wc.ISVNOptions;
033 import org.tmatesoft.svn.core.wc.SVNClientManager;
034 import org.tmatesoft.svn.core.wc.SVNConflictChoice;
035 import org.tmatesoft.svn.core.wc.SVNCopySource;
036 import org.tmatesoft.svn.core.wc.SVNRevision;
037 import org.tmatesoft.svn.core.wc.SVNStatus;
038 import org.tmatesoft.svn.core.wc.SVNUpdateClient;
039 import org.tmatesoft.svn.core.wc.SVNWCUtil;
040
041 /**
042 * This class is a thin layer over the SVNKit working copy API. All methods that
043 * trigger write operations on the sandbox tree are synchronized, but to ensure
044 * that there is not more than one Sandbox associated with a particular
045 * directory tree on disk the {@link SandboxManager} class should be used.
046 */
047 public class Sandbox {
048 /** Logger. */
049 static Logger lgr = Logger.getLogger(Sandbox.class);
050
051 /** Client manager. */
052 SVNClientManager clientManager;
053
054 /** SVN event handler. */
055 CommitEventHandler commitEventHandler = new CommitEventHandler(this);
056
057 /** SVN event handler. */
058 UpdateEventHandler updateEventHandler = new UpdateEventHandler(this);
059
060 /** SVN event handler. */
061 SandboxEventHandler sandboxEventHandler = new SandboxEventHandler(this);
062
063 /** Flag to indicate that the SVNKit library has been initialised. */
064 static boolean svnkitIsInitialised = false;
065
066 /**
067 * Flag to indicate whether this sandbox has been closed. If so, all future
068 * requests will throw an exception.
069 */
070 private volatile boolean closed = false;
071
072 /**
073 * Mutex used to synchronize access to methods of this class in a more
074 * controllable way than simple synchronization.
075 */
076 private ReentrantLock mutex = new ReentrantLock(true);
077
078 /**
079 * Flag to indicate whether the currently running operation (if any) should
080 * be allowed to be cancelled before it has completed normally. Generally
081 * speaking, background update operations such as regular wiki updates in CoW
082 * would be pre-emptible, but smaller operations such as those arising from a
083 * wiki editing session would not be pre-emptible. <i>Implementation
084 * note:</i> The value of this field should only be modified by a thread that
085 * holds the {@link #mutex}.
086 */
087 private boolean currentOperationPreemptible = false;
088
089 /** Construction. Not appropriate if authentication is needed. */
090 public Sandbox() {
091 this("", "", null);
092 } // Sandbox()
093
094 /** Construction. */
095 public Sandbox(String name, String password) {
096 this(name,password,null);
097 }
098
099 public Sandbox(String name, String password, File svnconfig) {
100 Sandbox.init();
101
102 /*
103 * Create a default run-time configuration options driver using the the
104 * Subversion run-time configuration area.
105 */
106 ISVNOptions options = SVNWCUtil.createDefaultOptions(svnconfig,true);
107
108 /*
109 * Creates an instance of SVNClientManager providing authentication
110 * information (name, password) and an options driver
111 */
112 clientManager =
113 SVNClientManager.newInstance((DefaultSVNOptions)options, name, password);
114
115 initEventHandlers();
116 } // Sandbox(String, String)
117
118 /** Construction with a custom authentication manager. */
119 public Sandbox(ISVNAuthenticationManager authManager) {
120 this(authManager,null);
121 }
122
123 public Sandbox(ISVNAuthenticationManager authManager, File svnconfig) {
124 Sandbox.init();
125
126 /*
127 * Create a default run-time configuration options driver using the the
128 * Subversion run-time configuration area.
129 */
130 ISVNOptions options = SVNWCUtil.createDefaultOptions(svnconfig,true);
131
132 /*
133 * Creates an instance of SVNClientManager providing authentication
134 * information (name, password) and an options driver
135 */
136 clientManager = SVNClientManager.newInstance(options, authManager);
137
138 initEventHandlers();
139 } // Sandbox(String, String)
140
141 private void initEventHandlers() {
142 /* Sets a custom event handler for an SVNCommitClient instance */
143 clientManager.getCommitClient().setEventHandler(commitEventHandler);
144
145 /* Sets a custom event handler for an SVNUpdateClient instance */
146 clientManager.getUpdateClient().setEventHandler(updateEventHandler);
147
148 /* Sets a custom event handler for an SVNWCClient instance */
149 clientManager.getWCClient().setEventHandler(sandboxEventHandler);
150 }
151
152 /**
153 * Initializes SVNKit to work with an SVN repository via various protocols.
154 */
155 synchronized private static void init() {
156 if(svnkitIsInitialised) return;
157
158 /* For using over http:// and https:// */
159 DAVRepositoryFactory.setup();
160
161 /* For using over svn:// and svn+xxx:// */
162 SVNRepositoryFactoryImpl.setup();
163
164 /* For using over file:/// */
165 FSRepositoryFactory.setup();
166
167 svnkitIsInitialised = true;
168 } // init()
169
170 /**
171 * Cancel any running preemptible operation, lock the mutex, reset the cancel
172 * flag and mark the current operation as pre-emptible or not according to
173 * the specified parameter.
174 */
175 private final void lock(boolean preemptible) {
176 // the following line may block if there is a running operation. If the
177 // running operation is preemptible then it should be cancelled shortly
178 mutex.lock();
179 currentOperationPreemptible = preemptible;
180 }
181
182 /**
183 * Unlock the mutex.
184 */
185 private final void unlock() {
186 mutex.unlock();
187 }
188
189
190 /**
191 * Close this sandbox, preventing any future operations. This method locks
192 * the mutex before setting the close flag, so will wait for any currently
193 * running operation to complete before returning.
194 */
195 public void close() {
196 lock(false);
197 try {
198 closed = true;
199 clientManager.dispose();
200 }
201 finally {
202 unlock();
203 }
204 }
205
206 /**
207 * Creates a new version controlled directory (doesn't create any intermediate
208 * directories) right in a repository. Like 'svn mkdir URL -m "some comment"'
209 * command. It's done by invoking
210 *
211 * SVNCommitClient.doMkDir(SVNURL[] urls, String commitMessage)
212 *
213 * which takes the following parameters:
214 *
215 * urls - an array of URLs that are to be created;
216 *
217 * commitMessage - a commit log message since a URL-based directory creation
218 * is immediately committed to a repository.
219 *
220 * @return SVNCommitInfo containing information on the new revision committed
221 * (revision number, etc.)
222 */
223 public SVNCommitInfo makeDirectory(SVNURL url,
224 String commitMessage) throws SVNException {
225 lock(false);
226 try {
227 if(closed) throw new IllegalStateException("Sandbox closed");
228 return clientManager.getCommitClient().doMkDir(new SVNURL[]{url},
229 commitMessage);
230 }
231 finally {
232 unlock();
233 }
234 }
235
236 /**
237 * Imports an unversioned directory into a repository location denoted by a
238 * destination URL (all necessary parent non-existent paths will be created
239 * automatically). This operation commits the repository to a new revision.
240 * Like 'svn import PATH URL (-N) -m "some comment"' command. It's done by
241 * invoking
242 *
243 * SVNCommitClient.doImport(File path, SVNURL dstURL, String commitMessage,
244 * boolean recursive)
245 *
246 * which takes the following parameters:
247 *
248 * path - a local unversioned directory or singal file that will be imported
249 * into a repository;
250 *
251 * dstURL - a repository location where the local unversioned directory/file
252 * will be imported into; this URL path may contain non-existent parent paths
253 * that will be created by the repository server;
254 *
255 * commitMessage - a commit log message since the new directory/file are
256 * immediately created in the repository;
257 *
258 * recursive - if true and path parameter corresponds to a directory then the
259 * directory will be added with all its child subdirictories, otherwise the
260 * operation will cover only the directory itself (only those files which are
261 * located in the directory).
262 *
263 * @return SVNCommitInfo containing information on the new revision committed
264 * (revision number, etc.).
265 */
266 public SVNCommitInfo importDirectory(File localPath,
267 SVNURL dstURL, String commitMessage, boolean isRecursive)
268 throws SVNException {
269 lock(false);
270 try {
271 if(closed) throw new IllegalStateException("Sandbox closed");
272 return clientManager.getCommitClient().doImport(localPath, dstURL,
273 commitMessage, null, true, false, SVNDepth.fromRecurse(isRecursive));
274 }
275 finally {
276 unlock();
277 }
278 } // importDirectory
279
280 /**
281 * Committs changes in a working copy to a repository. Like 'svn commit PATH
282 * -m "some comment"' command. It's done by invoking
283 *
284 * SVNCommitClient.doCommit(File[] paths, boolean keepLocks, String
285 * commitMessage, boolean force, boolean recursive)
286 *
287 * which takes the following parameters:
288 *
289 * paths - working copy paths which changes are to be committed;
290 *
291 * keepLocks - if true then doCommit(..) won't unlock locked paths; otherwise
292 * they will be unlocked after a successful commit;
293 *
294 * commitMessage - a commit log message;
295 *
296 * force - if true then a non-recursive commit will be forced anyway;
297 *
298 * recursive - if true and a path corresponds to a directory then doCommit(..)
299 * recursively commits changes for the entire directory, otherwise - only for
300 * child entries of the directory;
301 *
302 * @return SVNCommitInfo containing information on the new revision committed
303 * (revision number, etc.).
304 */
305 public SVNCommitInfo commit(File[] wcPath, boolean keepLocks,
306 String commitMessage) throws SVNException {
307 lock(false);
308 try {
309 if(closed) throw new IllegalStateException("Sandbox closed");
310 return clientManager.getCommitClient().doCommit(wcPath, keepLocks,
311 commitMessage, null, null, false, false, SVNDepth.fromRecurse(true));
312 }
313 finally {
314 unlock();
315 }
316 } // commit
317
318 /**
319 * Checks out a working copy from a repository. Like 'svn checkout URL[@REV]
320 * PATH (-r..)' command; It's done by invoking
321 *
322 * SVNUpdateClient.doCheckout(SVNURL url, File dstPath, SVNRevision
323 * pegRevision, SVNRevision revision, SVNDepth depth, boolean
324 * allowUnversionedObstructions)
325 *
326 * which takes the following parameters:
327 *
328 * url - a repository location from where a working copy is to be checked out;
329 *
330 * dstPath - a local path where the working copy will be fetched into;
331 *
332 * pegRevision - an SVNRevision representing a revision to concretize url
333 * (what exactly URL a user means and is sure of being the URL he needs); in
334 * other words that is the revision in which the URL is first looked up;
335 *
336 * revision - a revision at which a working copy being checked out is to be;an
337 *
338 * depth - whether to check out the whole tree rooted at that node, just the
339 * directory with no contents, just the directory and its immediate children,
340 * etc. We pass depth INFINITY if recursive == true and depth EMPTY otherwise.
341 *
342 * allowUnversionedObstructions - how to handle the case when there is already
343 * a file in the directory into which we are checking out that has the same
344 * name as something from the repository. We pass false, which means the
345 * checkout fails in this case.
346 *
347 * @return long the number of the revision the sandbox is at
348 */
349 public long checkout(SVNURL url, SVNRevision revision,
350 File destPath, boolean isRecursive) throws SVNException {
351 lock(false);
352 try {
353 if(closed) throw new IllegalStateException("Sandbox closed");
354
355 SVNUpdateClient updateClient = clientManager.getUpdateClient();
356
357 /* sets externals not to be ignored during the checkout */
358 updateClient.setIgnoreExternals(false);
359
360 return updateClient.doCheckout(url, destPath, revision, revision,
361 isRecursive ? SVNDepth.INFINITY : SVNDepth.EMPTY, false);
362 }
363 finally {
364 unlock();
365 }
366 } // checkout
367
368 /**
369 * Updates a working copy in a non-preemptible operation.
370 *
371 * @see #update(File,SVNRevision,boolean,boolean)
372 */
373 public long update(File wcPath, SVNRevision updateToRevision,
374 boolean isRecursive) throws SVNException {
375 return update(wcPath, updateToRevision, isRecursive, false);
376 }
377
378 /**
379 * Updates a working copy (brings changes from the repository into the working
380 * copy). Like 'svn update PATH' command; It's done by invoking
381 *
382 * SVNUpdateClient.doUpdate(File file, SVNRevision revision, boolean
383 * recursive)
384 *
385 * which takes the following parameters:
386 *
387 * file - a working copy entry that is to be updated;
388 *
389 * revision - a revision to which a working copy is to be updated;
390 *
391 * recursive - if true and an entry is a directory then doUpdate(..)
392 * recursively updates the entire directory, otherwise - only child entries of
393 * the directory.
394 *
395 * @param preemptible whether this update can be pre-empted by a "more
396 * important" update on the same sandbox. Typically this would be set to
397 * true for background update jobs that update a whole sandbox and false for
398 * interactive jobs that just update particular files.
399 * @return long the number of the revision the sandbox was updated to
400 */
401 public long update(File wcPath, SVNRevision updateToRevision,
402 boolean isRecursive, boolean preemptible) throws SVNException {
403 lock(preemptible);
404 try {
405 if(closed) throw new IllegalStateException("Sandbox closed");
406 // pro-actively check for cancellation so we get out of the way if
407 // someone else is waiting.
408 checkCancelled();
409 SVNUpdateClient updateClient = clientManager.getUpdateClient();
410
411 /* sets externals not to be ignored during the update */
412 updateClient.setIgnoreExternals(false);
413
414 return updateClient.doUpdate(wcPath, updateToRevision, SVNDepth
415 .fromRecurse(isRecursive), false, false);
416 }
417 finally {
418 unlock();
419 }
420 } // update
421
422 /**
423 * Updates a working copy to a different URL. Like 'svn switch URL' command.
424 * It's done by invoking
425 *
426 * SVNUpdateClient.doSwitch(File file, SVNURL url, SVNRevision revision,
427 * boolean recursive)
428 *
429 * which takes the following parameters:
430 *
431 * file - a working copy entry that is to be switched to a new url;
432 *
433 * url - a target URL a working copy is to be updated against;
434 *
435 * revision - a revision to which a working copy is to be updated;
436 *
437 * recursive - if true and an entry (file) is a directory then doSwitch(..)
438 * recursively switches the entire directory, otherwise - only child entries
439 * of the directory.
440 *
441 * @return long the number of the revision the sandbox was updated to
442 */
443 public long switchToURL(File wcPath, SVNURL url,
444 SVNRevision updateToRevision, boolean isRecursive) throws SVNException {
445 lock(false);
446 try {
447 if(closed) throw new IllegalStateException("Sandbox closed");
448
449 SVNUpdateClient updateClient = clientManager.getUpdateClient();
450
451 /* sets externals not to be ignored during the switch */
452 updateClient.setIgnoreExternals(false);
453
454 return updateClient.doSwitch(wcPath, url, SVNRevision.UNDEFINED,
455 updateToRevision, SVNDepth.fromRecurse(isRecursive), false, false);
456 }
457 finally {
458 unlock();
459 }
460 } // switchToURL
461
462 /**
463 * Updates a working copy in the same was as 'svn switch --relocate'
464 **/
465 public void relocate(File wcPath, SVNURL oldURL, SVNURL newURL)
466 throws SVNException {
467 lock(false);
468 try {
469 if(closed) throw new IllegalStateException("Sandbox closed");
470
471 SVNUpdateClient updateClient = clientManager.getUpdateClient();
472
473 /* sets externals not to be ignored during the switch */
474 updateClient.setIgnoreExternals(false);
475
476 updateClient.doRelocate(wcPath, oldURL, newURL, false);
477 }
478 finally {
479 unlock();
480 }
481 }
482
483 /**
484 * Displays status information on local path(s). Like 'svn status (-u) (-N)'
485 * command. It's done by invoking
486 *
487 * SVNStatusClient.doStatus(File path, boolean recursive, boolean remote,
488 * boolean reportAll, boolean includeIgnored, boolean collectParentExternals,
489 * ISVNStatusHandler handler)
490 *
491 * which takes the following parameters:
492 *
493 * path - an entry which status info to be gathered;
494 *
495 * recursive - if true and an entry is a directory then doStatus(..) collects
496 * status info not only for that directory but for each item inside stepping
497 * down recursively;
498 *
499 * remote - if true then doStatus(..) will cover the repository (not only the
500 * working copy) as well to find out what entries are out of date;
501 *
502 * reportAll - if true then doStatus(..) will also include unmodified entries;
503 *
504 * includeIgnored - if true then doStatus(..) will also include entries being
505 * ignored;
506 *
507 * collectParentExternals - if true then externals definitions won't be
508 * ignored;
509 *
510 * handler - an implementation of ISVNStatusHandler to process status info per
511 * each entry doStatus(..) traverses; such info is collected in an SVNStatus
512 * object and is passed to a handler's handleStatus(SVNStatus status) method
513 * where an implementor decides what to do with it.
514 */
515 public void showStatus(File wcPath, boolean isRecursive, boolean isRemote,
516 boolean isReportAll, boolean isIncludeIgnored,
517 boolean isCollectParentExternals) throws SVNException {
518 lock(true);
519 try {
520 /*
521 * StatusHandler displays status information for each entry in the console
522 * (in the manner of the native Subversion command line client)
523 */
524 clientManager.getStatusClient().doStatus(wcPath, SVNRevision.HEAD,
525 SVNDepth.fromRecurse(isRecursive), isRemote, isReportAll,
526 isIncludeIgnored, isCollectParentExternals, new StatusHandler(isRemote),
527 null);
528 }
529 finally {
530 unlock();
531 }
532 } // showStatus
533
534 /**
535 * Collects status information on local path(s). It's done by invoking
536 *
537 * SVNStatusClient.doStatus(File path, boolean remote) which takes the
538 * following parameters:
539 *
540 * path - an entry which status info to be gathered;
541 *
542 * remote - if true then doStatus(..) will cover the repository (not only the
543 * working copy) as well to find out what entries are out of date;
544 */
545 public SVNStatus doStatus(File wcPath, boolean isRemote) throws SVNException {
546 lock(false);
547 try {
548 return clientManager.getStatusClient().doStatus(wcPath, isRemote);
549 }
550 finally {
551 unlock();
552 }
553 } // doStatus
554
555 /**
556 * Resolve a conflict on local path(s). It's done by invoking
557 *
558 * SVNStatusClient.doResolve(File path, boolean recursive) which takes the
559 * following parameters:
560 *
561 * path - an entry which status info to be gathered;
562 *
563 * recursive - if true then doResolve(..) will cover the repository (stepping
564 * down recursively)
565 */
566 public void doResolve(File wcPath, boolean isRecursive) throws SVNException {
567 lock(false);
568 try {
569 if(closed) throw new IllegalStateException("Sandbox closed");
570 clientManager.getWCClient().doResolve(wcPath,
571 SVNDepth.fromRecurse(isRecursive), SVNConflictChoice.MERGED);
572 }
573 finally {
574 unlock();
575 }
576 } // doResolve
577
578 /**
579 * Revert uncommitted changes. It's done by invoking
580 *
581 * SVNStatusClient.doRevert(File[] wcPaths, SVNDepth depth,
582 * Collection changelists) which takes the
583 * following parameters:
584 *
585 * wcPaths - paths of the entries to revert
586 *
587 * depth - we pass SVNDepth.INFINITY, which means that if any of the wcPaths
588 * are directories (as opposed to normal files) their contents will be
589 * reverted recursively.
590 *
591 * changelists - a collection of String changelist names - we pass null,
592 * which means changelists are ignored.
593 */
594 public void revert(File... wcPaths) throws SVNException {
595 lock(false);
596 try {
597 if(closed) throw new IllegalStateException("Sandbox closed");
598 clientManager.getWCClient().doRevert(wcPaths, SVNDepth.INFINITY, null);
599 }
600 finally {
601 unlock();
602 }
603 } // revert
604
605 /**
606 * Collects information on local path(s). Like 'svn info (-R)' command. It's
607 * done by invoking
608 *
609 * SVNWCClient.doInfo(File path, SVNRevision revision, boolean recursive,
610 * ISVNInfoHandler handler)
611 *
612 * which takes the following parameters:
613 *
614 * path - a local entry for which info will be collected;
615 *
616 * revision - a revision of an entry which info is interested in; if it's not
617 * WORKING then info is got from a repository;
618 *
619 * recursive - if true and an entry is a directory then doInfo(..) collects
620 * info not only for that directory but for each item inside stepping down
621 * recursively;
622 *
623 * handler - an implementation of ISVNInfoHandler to process info per each
624 * entry doInfo(..) traverses; such info is collected in an SVNInfo object and
625 * is passed to a handler's handleInfo(SVNInfo info) method where an
626 * implementor decides what to do with it.
627 */
628 public void showInfo(File wcPath, SVNRevision revision, boolean isRecursive)
629 throws SVNException {
630 lock(true);
631 try {
632 /*
633 * InfoHandler displays information for each entry in the console (in the
634 * manner of the native Subversion command line client)
635 */
636 clientManager.getWCClient().doInfo(wcPath, SVNRevision.UNDEFINED, revision,
637 SVNDepth.fromRecurse(isRecursive), null, new InfoHandler());
638 }
639 finally {
640 unlock();
641 }
642 } // showInfo
643
644 /**
645 * Puts directories and files under version control scheduling them for
646 * addition to a repository. They will be added in a next commit. Like 'svn
647 * add PATH' command. It's done by invoking
648 *
649 * SVNWCClient.doAdd(File path, boolean force, boolean mkdir, boolean
650 * climbUnversionedParents, boolean recursive)
651 *
652 * which takes the following parameters:
653 *
654 * path - an entry to be scheduled for addition;
655 *
656 * force - set to true to force an addition of an entry anyway;
657 *
658 * mkdir - if true doAdd(..) creates an empty directory at path and schedules
659 * it for addition, like 'svn mkdir PATH' command;
660 *
661 * climbUnversionedParents - if true and the parent of the entry to be
662 * scheduled for addition is not under version control, then doAdd(..)
663 * automatically schedules the parent for addition, too;
664 *
665 * recursive - if true and an entry is a directory then doAdd(..) recursively
666 * schedules all its inner dir entries for addition as well.
667 */
668 public void addEntry(File wcPath) throws SVNException {
669 addEntry(wcPath, true);
670 } // addEntry
671
672 /**
673 * Puts directories and files under version control scheduling them for
674 * addition to a repository. They will be added in a next commit. Like 'svn
675 * add PATH' command.
676 *
677 * @param wcPath the file or directory to add. Its parent must already be
678 * known to subversion (though not necessarily committed).
679 * @param recurse if true, and wcPath names a directory, recursively add all
680 * the directories contents. If false, just add the directory itself
681 * but not its contents. Has no effect if wcPath names a normal
682 * file. Note this is different from the recurse parameter to most
683 * other methods in this class, where non-recursive includes files in
684 * the directory but not subdirectories.
685 */
686 public void addEntry(File wcPath, boolean recurse) throws SVNException {
687 lock(false);
688 try {
689 if(closed) throw new IllegalStateException("Sandbox closed");
690 clientManager.getWCClient().doAdd(wcPath, false, false, false,
691 (recurse ? SVNDepth.INFINITY : SVNDepth.EMPTY), false, false);
692 }
693 finally {
694 unlock();
695 }
696 } // addEntry
697
698 /**
699 * Locks working copy paths, so that no other user can commit changes to them.
700 * Like 'svn lock PATH' command. It's done by invoking
701 *
702 * SVNWCClient.doLock(File[] paths, boolean stealLock, String lockMessage)
703 *
704 * which takes the following parameters:
705 *
706 * paths - an array of local entries to be locked;
707 *
708 * stealLock - set to true to steal the lock from another user or working
709 * copy;
710 *
711 * lockMessage - an optional lock comment string.
712 */
713 public void lock(File wcPath, boolean isStealLock,
714 String lockComment) throws SVNException {
715 lock(false);
716 try {
717 if(closed) throw new IllegalStateException("Sandbox closed");
718 clientManager.getWCClient().doLock(new File[]{wcPath}, isStealLock,
719 lockComment);
720 }
721 finally {
722 unlock();
723 }
724 } // lock
725
726 /**
727 * Schedules directories and files for deletion from version control upon the
728 * next commit (locally). Like 'svn delete PATH' command. It's done by
729 * invoking
730 *
731 * SVNWCClient.doDelete(File path, boolean force, boolean dryRun)
732 *
733 * which takes the following parameters:
734 *
735 * path - an entry to be scheduled for deletion;
736 *
737 * force - a boolean flag which is set to true to force a deletion even if an
738 * entry has local modifications;
739 *
740 * dryRun - set to true not to delete an entry but to check if it can be
741 * deleted; if false - then it's a deletion itself.
742 */
743 public void delete(File wcPath, boolean force)
744 throws SVNException {
745 lock(false);
746 try {
747 if(closed) throw new IllegalStateException("Sandbox closed");
748 clientManager.getWCClient().doDelete(wcPath, force, false);
749 }
750 finally {
751 unlock();
752 }
753 } // delete
754
755 /**
756 * Duplicates srcURL to dstURL (URL to URL) preserving history. Like 'svn copy
757 * srcURL dstURL -m "some comment"' command. It's done by invoking
758 *
759 * doCopy(SVNURL srcURL, SVNRevision srcRevision, SVNURL dstURL, boolean
760 * isMove, String commitMessage)
761 *
762 * which takes the following parameters:
763 *
764 * srcURL - a source URL that is to be copied;
765 *
766 * srcRevision - a definite revision of srcURL
767 *
768 * dstURL - a URL where srcURL will be copied; if srcURL and dstURL are both
769 * directories then there are two cases: a) dstURL already exists - then
770 * doCopy(..) will duplicate the entire source directory and put it inside
771 * dstURL (for example, consider srcURL = svn://localhost/rep/MyRepos, dstURL
772 * = svn://localhost/rep/MyReposCopy, in this case if doCopy(..) succeeds
773 * MyRepos will be in MyReposCopy - svn://localhost/rep/MyReposCopy/MyRepos);
774 * b) dstURL doesn't exist yet - then doCopy(..) will create a directory and
775 * recursively copy entries from srcURL into dstURL (for example, consider the
776 * same srcURL = svn://localhost/rep/MyRepos, dstURL =
777 * svn://localhost/rep/MyReposCopy, in this case if doCopy(..) succeeds
778 * MyRepos entries will be in MyReposCopy, like:
779 * svn://localhost/rep/MyRepos/Dir1 ....
780 * svn://localhost/rep/MyReposCopy/Dir1...);
781 *
782 * isMove - if false then srcURL is only copied to dstURL what corresponds to
783 * 'svn copy srcURL dstURL -m "some comment"'; but if it's true then srcURL
784 * will be copied and deleted - 'svn move srcURL dstURL -m "some comment"';
785 *
786 * commitMessage - a commit log message since URL/URL copying is immediately
787 * committed to a repository.
788 *
789 * @return SVNCommitInfo containing information on the new revision.
790 */
791 public SVNCommitInfo copy(SVNURL srcURL, SVNURL dstURL,
792 boolean isMove, String commitMessage) throws SVNException {
793 lock(false);
794 try {
795 if(closed) throw new IllegalStateException("Sandbox closed");
796
797 // introduced to support SVNKit 1.2.0
798 SVNCopySource source =
799 new SVNCopySource(SVNRevision.UNDEFINED, SVNRevision.HEAD, srcURL);
800
801 return clientManager.getCopyClient().doCopy(new SVNCopySource[]{source},
802 dstURL, isMove, false, true, commitMessage, null);
803 }
804 finally {
805 unlock();
806 }
807 } // copy
808
809 /**
810 * Returns the URL corresponding to the repository location of the given
811 * working copy file.
812 *
813 * @param file
814 * the file, which must be under version control in a sandbox. The
815 * file need not be checked in but it must be known to subversion
816 * (e.g. it could be added but not yet committed).
817 * @return the repository URL
818 */
819 public SVNURL getRepositoryURL(File file) throws SVNException {
820 lock(false);
821 try {
822 return clientManager.getWCClient().doInfo(file, SVNRevision.WORKING)
823 .getURL();
824 }
825 finally {
826 unlock();
827 }
828 }
829
830 /**
831 * Returns the URL of the root of the repository containing the given working
832 * copy file.
833 *
834 * @param file
835 * the file, which must be under version control in a sandbox. The
836 * file need not be checked in but it must be known to subversion
837 * (e.g. it could be added but not yet committed).
838 * @return the repository root URL
839 */
840 public SVNURL getRepositoryRoot(File file) throws SVNException {
841 lock(false);
842 try {
843 return clientManager.getWCClient().getReposRoot(file,
844 getRepositoryURL(file), SVNRevision.WORKING, null, null);
845 }
846 finally {
847 unlock();
848 }
849 }
850
851
852 /**
853 * If the current operation is preemptible and any other operations are
854 * waiting, throw an SVNCancelException.
855 */
856 public void checkCancelled() throws SVNCancelException {
857 if(currentOperationPreemptible && mutex.hasQueuedThreads()) {
858 throw new SVNCancelException();
859 }
860 }
861 } // Sandbox
|