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 import org.tmatesoft.svn.core.wc.SVNStatusClient;
41 import org.tmatesoft.svn.core.wc.SVNUpdateClient;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class SVNResource extends AbstractFilteredResource {
60
61
62 private final Logger log = LoggerFactory.getLogger(SVNResource.class);
63
64
65 private final SVNClientManager clientManager;
66
67
68 private SVNURL remoteRepository;
69
70
71 private File workingCopy;
72
73
74 private SVNRevision revision;
75
76
77 private String resourceFileName;
78
79
80
81
82
83
84
85
86
87
88
89
90 public SVNResource(SVNClientManager svnClientMgr, SVNURL repositoryUrl, File workingCopyDirectory,
91 long workingRevision, String resourceFile) throws ResourceException {
92 DAVRepositoryFactory.setup();
93 SVNRepositoryFactoryImpl.setup();
94 FSRepositoryFactory.setup();
95 if (svnClientMgr == null) {
96 log.error("SVN client manager may not be null");
97 throw new IllegalArgumentException("SVN client manager may not be null");
98 }
99 clientManager = svnClientMgr;
100
101 if (repositoryUrl == null) {
102 throw new IllegalArgumentException("SVN repository URL may not be null");
103 }
104 remoteRepository = repositoryUrl;
105
106 try {
107 checkWorkingCopyDirectory(workingCopyDirectory);
108 workingCopy = workingCopyDirectory;
109 } catch (ResourceException e) {
110 throw new IllegalArgumentException(e.getMessage());
111 }
112
113 if (workingRevision < 0) {
114 this.revision = SVNRevision.HEAD;
115 } else {
116 this.revision = SVNRevision.create(workingRevision);
117 }
118
119 resourceFileName = DatatypeHelper.safeTrimOrNullString(resourceFile);
120 if (resourceFileName == null) {
121 log.error("SVN working copy resource file name may not be null or empty");
122 throw new IllegalArgumentException("SVN working copy resource file name may not be null or empty");
123 }
124
125 checkoutOrUpdateResource();
126 if (!getResourceFile().exists()) {
127 log.error("Resource file " + resourceFile + " does not exist in SVN working copy directory "
128 + workingCopyDirectory.getAbsolutePath());
129 throw new ResourceException("Resource file " + resourceFile
130 + " does not exist in SVN working copy directory " + workingCopyDirectory.getAbsolutePath());
131 }
132 }
133
134
135 public boolean exists() throws ResourceException {
136 return getResourceFile().exists();
137 }
138
139
140 public InputStream getInputStream() throws ResourceException {
141 try {
142 return applyFilter(new FileInputStream(getResourceFile()));
143 } catch (IOException e) {
144 String erroMsg = "Unable to read resource file " + resourceFileName + " from local working copy "
145 + workingCopy.getAbsolutePath();
146 log.error(erroMsg, e);
147 throw new ResourceException(erroMsg, e);
148 }
149 }
150
151
152 public DateTime getLastModifiedTime() throws ResourceException {
153 SVNStatusClient client = clientManager.getStatusClient();
154 client.setIgnoreExternals(false);
155
156 try {
157 SVNStatusHandler handler = new SVNStatusHandler();
158 client.doStatus(getResourceFile(), revision, SVNDepth.INFINITY, true, true, false, false, handler, null);
159 SVNStatus status = handler.getStatus();
160
161
162 if (status.getRemoteRevision() == null) {
163 return new DateTime(status.getCommittedDate());
164 } else {
165 return new DateTime(status.getRemoteDate());
166 }
167 } catch (SVNException e) {
168 String errMsg = "Unable to check status of resource " + resourceFileName + " within working directory "
169 + workingCopy.getAbsolutePath();
170 log.error(errMsg, e);
171 throw new ResourceException(errMsg, e);
172 }
173 }
174
175
176 public String getLocation() {
177 return remoteRepository.toDecodedString() + "/" + resourceFileName;
178 }
179
180
181
182
183
184
185
186
187 protected void checkWorkingCopyDirectory(File directory) throws ResourceException {
188 if (directory == null) {
189 log.error("SVN working copy directory may not be null");
190 throw new ResourceException("SVN working copy directory may not be null");
191 }
192
193 if (!directory.exists()) {
194 boolean created = directory.mkdirs();
195 if (!created) {
196 log.error("SVN working copy direction " + directory.getAbsolutePath()
197 + " does not exist and could not be created");
198 throw new ResourceException("SVN working copy direction " + directory.getAbsolutePath()
199 + " does not exist and could not be created");
200 }
201 }
202
203 if (!directory.isDirectory()) {
204 log.error("SVN working copy location " + directory.getAbsolutePath() + " is not a directory");
205 throw new ResourceException("SVN working copy location " + directory.getAbsolutePath()
206 + " is not a directory");
207 }
208
209 if (!directory.canRead()) {
210 log.error("SVN working copy directory " + directory.getAbsolutePath() + " can not be read by this process");
211 throw new ResourceException("SVN working copy directory " + directory.getAbsolutePath()
212 + " can not be read by this process");
213 }
214
215 if (!directory.canWrite()) {
216 log.error("SVN working copy directory " + directory.getAbsolutePath()
217 + " can not be written to by this process");
218 throw new ResourceException("SVN working copy directory " + directory.getAbsolutePath()
219 + " can not be written to by this process");
220 }
221 }
222
223
224
225
226
227
228
229
230
231 protected void checkoutOrUpdateResource() throws ResourceException {
232 SVNUpdateClient client = clientManager.getUpdateClient();
233 client.setIgnoreExternals(false);
234
235 File svnMetadataDir = new File(workingCopy, ".svn");
236 if (!svnMetadataDir.exists()) {
237 try {
238 client.doCheckout(remoteRepository, workingCopy, revision, revision, SVNDepth.INFINITY, true);
239 } catch (SVNException e) {
240 String errMsg = "Unable to check out revsion " + revision.toString() + " from remote repository "
241 + remoteRepository.toDecodedString() + " to local working directory "
242 + workingCopy.getAbsolutePath();
243 log.error(errMsg, e);
244 throw new ResourceException(errMsg, e);
245 }
246 } else {
247 try {
248 client.doUpdate(workingCopy, revision, SVNDepth.INFINITY, true, true);
249 } catch (SVNException e) {
250 String errMsg = "Unable to update working copy of resoure " + remoteRepository.toDecodedString()
251 + " in working copy " + workingCopy.getAbsolutePath() + " to revsion " + revision.toString();
252 log.error(errMsg, e);
253 throw new ResourceException(errMsg, e);
254 }
255 }
256 }
257
258
259
260
261
262
263 protected File getResourceFile() {
264 return new File(workingCopy, resourceFileName);
265 }
266
267
268 private class SVNStatusHandler implements ISVNStatusHandler {
269
270
271 private SVNStatus status;
272
273
274
275
276
277
278 public SVNStatus getStatus() {
279 return status;
280 }
281
282
283 public void handleStatus(SVNStatus currentStatus) throws SVNException {
284 status = currentStatus;
285 }
286 }
287 }