1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package edu.internet2.middleware.shibboleth.common.resource;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24
25 import org.joda.time.DateTime;
26 import org.opensaml.util.resource.AbstractFilteredResource;
27 import org.opensaml.util.resource.ResourceException;
28 import org.opensaml.xml.util.DatatypeHelper;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 import org.tmatesoft.svn.core.SVNDepth;
32 import org.tmatesoft.svn.core.SVNException;
33 import org.tmatesoft.svn.core.SVNURL;
34 import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
35 import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
36 import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
37 import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
38 import org.tmatesoft.svn.core.wc.SVNClientManager;
39 import org.tmatesoft.svn.core.wc.SVNRevision;
40 import org.tmatesoft.svn.core.wc.SVNStatus;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class SVNResource extends AbstractFilteredResource {
59
60
61 private final Logger log = LoggerFactory.getLogger(SVNResource.class);
62
63
64 private final SVNClientManager clientManager;
65
66
67 private SVNURL remoteRepository;
68
69
70 private File workingCopyDirectory;
71
72
73 private SVNRevision retrievalRevision;
74
75
76 private String resourceFileName;
77
78
79 private DateTime lastModified;
80
81
82
83
84
85
86
87
88
89
90
91
92 public SVNResource(SVNClientManager svnClientMgr, SVNURL repositoryUrl, File workingCopy, long workingRevision,
93 String resourceFile) throws ResourceException {
94 DAVRepositoryFactory.setup();
95 SVNRepositoryFactoryImpl.setup();
96 FSRepositoryFactory.setup();
97 if (svnClientMgr == null) {
98 log.error("SVN client manager may not be null");
99 throw new IllegalArgumentException("SVN client manager may not be null");
100 }
101 clientManager = svnClientMgr;
102
103 if (repositoryUrl == null) {
104 throw new IllegalArgumentException("SVN repository URL may not be null");
105 }
106 remoteRepository = repositoryUrl;
107
108 try {
109 checkWorkingCopyDirectory(workingCopy);
110 workingCopyDirectory = workingCopy;
111 } catch (ResourceException e) {
112 throw new IllegalArgumentException(e.getMessage());
113 }
114
115 if (workingRevision < 0) {
116 this.retrievalRevision = SVNRevision.HEAD;
117 } else {
118 this.retrievalRevision = SVNRevision.create(workingRevision);
119 }
120
121 resourceFileName = DatatypeHelper.safeTrimOrNullString(resourceFile);
122 if (resourceFileName == null) {
123 log.error("SVN working copy resource file name may not be null or empty");
124 throw new IllegalArgumentException("SVN working copy resource file name may not be null or empty");
125 }
126
127 checkoutOrUpdateResource();
128 if (!getResourceFile().exists()) {
129 log.error("Resource file " + resourceFile + " does not exist in SVN working copy directory "
130 + workingCopy.getAbsolutePath());
131 throw new ResourceException("Resource file " + resourceFile
132 + " does not exist in SVN working copy directory " + workingCopy.getAbsolutePath());
133 }
134 }
135
136
137 public boolean exists() throws ResourceException {
138 return getResourceFile().exists();
139 }
140
141
142 public InputStream getInputStream() throws ResourceException {
143 checkoutOrUpdateResource();
144 try {
145 return applyFilter(new FileInputStream(getResourceFile()));
146 } catch (IOException e) {
147 String erroMsg = "Unable to read resource file " + resourceFileName + " from local working copy "
148 + workingCopyDirectory.getAbsolutePath();
149 log.error(erroMsg, e);
150 throw new ResourceException(erroMsg, e);
151 }
152 }
153
154
155 public DateTime getLastModifiedTime() throws ResourceException {
156 checkoutOrUpdateResource();
157 return lastModified;
158 }
159
160
161 public String getLocation() {
162 return remoteRepository.toDecodedString() + "/" + resourceFileName;
163 }
164
165
166
167
168
169
170
171
172 protected File getResourceFile() throws ResourceException {
173 return new File(workingCopyDirectory, resourceFileName);
174 }
175
176
177
178
179
180
181
182
183 protected void checkWorkingCopyDirectory(File directory) throws ResourceException {
184 if (directory == null) {
185 log.error("SVN working copy directory may not be null");
186 throw new ResourceException("SVN working copy directory may not be null");
187 }
188
189 if (!directory.exists()) {
190 boolean created = directory.mkdirs();
191 if (!created) {
192 log.error("SVN working copy direction " + directory.getAbsolutePath()
193 + " does not exist and could not be created");
194 throw new ResourceException("SVN working copy direction " + directory.getAbsolutePath()
195 + " does not exist and could not be created");
196 }
197 }
198
199 if (!directory.isDirectory()) {
200 log.error("SVN working copy location " + directory.getAbsolutePath() + " is not a directory");
201 throw new ResourceException("SVN working copy location " + directory.getAbsolutePath()
202 + " is not a directory");
203 }
204
205 if (!directory.canRead()) {
206 log.error("SVN working copy directory " + directory.getAbsolutePath() + " can not be read by this process");
207 throw new ResourceException("SVN working copy directory " + directory.getAbsolutePath()
208 + " can not be read by this process");
209 }
210
211 if (!directory.canWrite()) {
212 log.error("SVN working copy directory " + directory.getAbsolutePath()
213 + " can not be written to by this process");
214 throw new ResourceException("SVN working copy directory " + directory.getAbsolutePath()
215 + " can not be written to by this process");
216 }
217 }
218
219
220
221
222
223
224
225
226
227 protected void checkoutOrUpdateResource() throws ResourceException {
228 log.debug("checking out or updating working copy");
229 SVNRevision newRevision;
230
231 if (!workingCopyDirectoryExists()) {
232 log.debug("working copy does not yet exist, checking it out");
233 newRevision = checkoutResourceDirectory();
234 } else {
235 if (retrievalRevision != SVNRevision.HEAD) {
236 log.debug("Working copy exists and version is pegged at {}, no need to update",
237 retrievalRevision.toString());
238 return;
239 }
240 log.debug("Working copy exists, updating to latest version.");
241 newRevision = updateResourceDirectory();
242 }
243
244 log.debug("Determing last modification date of revision {}", newRevision.getNumber());
245 lastModified = getLastModificationForRevision(newRevision);
246 }
247
248
249
250
251
252
253 private boolean workingCopyDirectoryExists() {
254 File svnMetadataDir = new File(workingCopyDirectory, ".svn");
255 return svnMetadataDir.exists();
256 }
257
258
259
260
261
262
263
264
265 private SVNRevision checkoutResourceDirectory() throws ResourceException {
266 try {
267 long newRevision = clientManager.getUpdateClient().doCheckout(remoteRepository, workingCopyDirectory,
268 retrievalRevision, retrievalRevision, SVNDepth.INFINITY, true);
269 log.debug(
270 "Checked out revision {} from remote repository {} and stored it in local working directory {}",
271 new Object[] { newRevision, remoteRepository.toDecodedString(),
272 workingCopyDirectory.getAbsolutePath(), });
273 return SVNRevision.create(newRevision);
274 } catch (SVNException e) {
275 String errMsg = "Unable to check out revsion " + retrievalRevision.toString() + " from remote repository "
276 + remoteRepository.toDecodedString() + " to local working directory "
277 + workingCopyDirectory.getAbsolutePath();
278 log.error(errMsg, e);
279 throw new ResourceException(errMsg, e);
280 }
281 }
282
283
284
285
286
287
288
289
290 private SVNRevision updateResourceDirectory() throws ResourceException {
291 try {
292 long newRevision = clientManager.getUpdateClient().doUpdate(workingCopyDirectory, retrievalRevision,
293 SVNDepth.INFINITY, true, true);
294 log.debug("Updated local working directory {} to revision {} from remote repository {}", new Object[] {
295 workingCopyDirectory.getAbsolutePath(), newRevision, remoteRepository.toDecodedString(), });
296 return SVNRevision.create(newRevision);
297 } catch (SVNException e) {
298 String errMsg = "Unable to update working copy of resoure " + remoteRepository.toDecodedString()
299 + " in working copy " + workingCopyDirectory.getAbsolutePath() + " to revsion "
300 + retrievalRevision.toString();
301 log.error(errMsg, e);
302 throw new ResourceException(errMsg, e);
303 }
304 }
305
306
307
308
309
310
311
312
313
314
315 private DateTime getLastModificationForRevision(SVNRevision revision) throws ResourceException {
316 try {
317 SVNStatusHandler handler = new SVNStatusHandler();
318 clientManager.getStatusClient().doStatus(getResourceFile(), revision, SVNDepth.INFINITY, true, true, false,
319 false, handler, null);
320 SVNStatus status = handler.getStatus();
321
322
323 if (status.getRemoteRevision() == null) {
324 return new DateTime(status.getCommittedDate());
325 } else {
326 return new DateTime(status.getRemoteDate());
327 }
328 } catch (SVNException e) {
329 String errMsg = "Unable to check status of resource " + resourceFileName + " within working directory "
330 + workingCopyDirectory.getAbsolutePath();
331 log.error(errMsg, e);
332 throw new ResourceException(errMsg, e);
333 }
334 }
335
336
337 private class SVNStatusHandler implements ISVNStatusHandler {
338
339
340 private SVNStatus status;
341
342
343
344
345
346
347 public SVNStatus getStatus() {
348 return status;
349 }
350
351
352 public void handleStatus(SVNStatus currentStatus) throws SVNException {
353 status = currentStatus;
354 }
355 }
356 }