2 * Copyright (C) 2007-2012 Mathieu Baudier
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org
.argeo
.slc
.web
.ajaxplorer
.svn
;
20 import org
.argeo
.slc
.web
.ajaxplorer
.AjxpDriverException
;
21 import org
.argeo
.slc
.web
.ajaxplorer
.file
.FileDriver
;
22 import org
.springframework
.beans
.factory
.BeanNameAware
;
23 import org
.tmatesoft
.svn
.core
.SVNException
;
24 import org
.tmatesoft
.svn
.core
.SVNURL
;
25 import org
.tmatesoft
.svn
.core
.internal
.io
.fs
.FSRepositoryFactory
;
26 import org
.tmatesoft
.svn
.core
.io
.SVNRepository
;
27 import org
.tmatesoft
.svn
.core
.wc
.SVNClientManager
;
28 import org
.tmatesoft
.svn
.core
.wc
.SVNInfo
;
29 import org
.tmatesoft
.svn
.core
.wc
.SVNRevision
;
30 import org
.tmatesoft
.svn
.core
.wc
.admin
.SVNAdminClient
;
32 public class SvnDriver
extends FileDriver
implements BeanNameAware
{
33 private final static String DEFAULT_DATA_PATH
= System
34 .getProperty("user.home")
35 + File
.separator
+ "AjaXplorerArchiver" + File
.separator
+ "data";
37 private final static long WRITE_ACTION_TIMEOUT
= 10 * 60 * 1000;
39 private SVNURL baseUrl
;
40 private SVNClientManager manager
;
42 private String beanName
;
44 private boolean isInWriteAction
= false;
47 FSRepositoryFactory
.setup();
48 manager
= SVNClientManager
.newInstance();
50 String basePath
= getBasePath();
51 if (basePath
!= null) {
52 File baseDir
= new File(basePath
);
53 if (baseDir
.exists()) {// base dir exists
54 boolean shouldCheckOut
= baseDirChecks(baseDir
);
62 String defaultBasePath
= DEFAULT_DATA_PATH
+ File
.separator
63 + "svnwc" + File
.separator
+ beanName
;
64 log
.warn("No base path provided, use " + defaultBasePath
);
65 setBasePath(defaultBasePath
);
67 File baseDir
= new File(getBasePath());
68 if (!baseDir
.exists()) {
72 if (baseDirChecks(baseDir
)) {
73 if (getBaseUrl() == null) {
74 String defaultRepoPath
= DEFAULT_DATA_PATH
+ File
.separator
75 + "svnrepos" + File
.separator
+ beanName
;
76 log
.warn("No base URL found, create repository at "
78 baseUrl
= createRepository(new File(defaultRepoPath
));
80 checkOut(new File(getBasePath()));
83 log
.info("SVN driver initialized with base url " + getBaseUrl()
84 + " and base path " + getBasePath());
87 /** Builds a SVN URL. */
88 public SVNURL
getSVNURL(String relativePath
) {
90 return baseUrl
.appendPath(relativePath
, false);
91 } catch (SVNException e
) {
92 throw new AjxpDriverException(
93 "Cannot build URL from relative path " + relativePath
94 + " and base url " + baseUrl
);
98 public SVNRepository
getRepository() {
100 return manager
.createRepository(baseUrl
, true);
101 } catch (SVNException e
) {
102 throw new AjxpDriverException("Cannot create repository for "
108 * Verifies that the provided existing base dir is ok and whether one should
109 * check out. Set the base url from the working copy.
111 * @return whether one should check out.
113 protected boolean baseDirChecks(File baseDir
) {
114 if (!baseDir
.isDirectory()) {
115 throw new AjxpDriverException("Base path " + baseDir
116 + " is not a directory.");
119 try {// retrieves SVN infos
120 SVNInfo info
= manager
.getWCClient().doInfo(baseDir
,
121 SVNRevision
.WORKING
);
122 SVNURL baseUrlTemp
= info
.getURL();
123 if (baseUrl
!= null) {
124 if (!baseUrl
.equals(baseUrlTemp
)) {
125 throw new AjxpDriverException(
126 "SVN URL of the working copy "
128 + " is not compatible with provided baseUrl "
132 this.baseUrl
= baseUrlTemp
;
135 } catch (SVNException e
) {// no info retrieved
137 .warn("Could not retrieve SVN info from "
141 + "). Guess that it is and empty dir and try to check out from provided URL.");
142 if (baseDir
.listFiles().length
!= 0) {
143 throw new AjxpDriverException("Base dir " + baseDir
144 + " is not a working copy and not an empty dir.");
150 protected void checkOut(File baseDir
) {
151 if (getBaseUrl() == null) {
152 throw new AjxpDriverException(
153 "No SVN URL provided, cannot check out.");
156 // Make sure directory exists
160 long revision
= manager
.getUpdateClient().doCheckout(getBaseUrl(),
161 baseDir
, SVNRevision
.UNDEFINED
, SVNRevision
.HEAD
, true);
162 log
.info("Checked out from " + baseUrl
+ " to " + baseDir
163 + " at revision " + revision
);
164 } catch (SVNException e
) {
165 throw new AjxpDriverException("Cannot check out from " + baseUrl
166 + " to " + baseDir
, e
);
170 protected SVNURL
createRepository(File repoDir
) {
172 SVNAdminClient adminClient
= manager
.getAdminClient();
173 return adminClient
.doCreateRepository(repoDir
, null, true, false);
174 } catch (SVNException e
) {
175 throw new AjxpDriverException("Cannot create repository at "
180 private void updateIfRequired(File dir
) {
182 SVNInfo wcInfo
= manager
.getWCClient().doInfo(getBaseDir(),
183 SVNRevision
.WORKING
);
184 SVNRevision wcRev
= wcInfo
.getRevision();
185 SVNInfo repoInfo
= manager
.getWCClient().doInfo(getBaseUrl(),
186 null, SVNRevision
.HEAD
);
187 SVNRevision repoRev
= repoInfo
.getRevision();
189 if (log
.isTraceEnabled())
191 .trace("WC Revision=" + wcRev
+ ", Repo Revision="
194 if (!wcRev
.equals(repoRev
)) {
195 log
.debug("Update working copy from revision " + wcRev
196 + " to revision " + repoRev
);
197 manager
.getUpdateClient().doUpdate(getBaseDir(),
198 SVNRevision
.HEAD
, true);
200 } catch (SVNException e
) {
201 throw new AjxpDriverException("Cannot update working copy "
206 public synchronized void beginWriteAction(File dir
) {
207 if (isInWriteAction
) {
209 wait(WRITE_ACTION_TIMEOUT
);
210 } catch (InterruptedException e
) {
213 if (isInWriteAction
) {
214 throw new AjxpDriverException(
215 "Still in write action after timeout "
216 + WRITE_ACTION_TIMEOUT
+ " ms.");
220 isInWriteAction
= true;
221 updateIfRequired(dir
);
224 public synchronized void completeWriteAction(File dir
) {
225 isInWriteAction
= false;
229 public synchronized void rollbackWriteAction(File dir
) {
231 isInWriteAction
= false;
235 public void commitAll(String message
) throws SVNException
{
236 if(log
.isTraceEnabled())
237 log
.trace("SVN Commit: " + getBaseDir());
238 manager
.getCommitClient().doCommit(new File
[] { getBaseDir() }, true,
239 message
, true, true);
243 /** Spring bean name, set at initialization. */
244 public void setBeanName(String beanName
) {
245 this.beanName
= beanName
;
248 public void setBaseUrl(String baseUrl
) {
250 this.baseUrl
= SVNURL
.parseURIDecoded(baseUrl
);
251 } catch (SVNException e
) {
252 throw new AjxpDriverException("Cannot parse SVN URL " + baseUrl
, e
);
256 public SVNURL
getBaseUrl() {
260 public SVNClientManager
getManager() {