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