1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.common.config.resource;
18
19 import java.io.File;
20 import java.util.ArrayList;
21
22 import javax.xml.namespace.QName;
23
24 import org.opensaml.xml.util.DatatypeHelper;
25 import org.opensaml.xml.util.XMLHelper;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28 import org.springframework.beans.factory.BeanCreationException;
29 import org.springframework.beans.factory.support.AbstractBeanDefinition;
30 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
31 import org.springframework.beans.factory.xml.ParserContext;
32 import org.tmatesoft.svn.core.SVNException;
33 import org.tmatesoft.svn.core.SVNURL;
34 import org.tmatesoft.svn.core.auth.SVNAuthentication;
35 import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication;
36 import org.tmatesoft.svn.core.auth.SVNUserNameAuthentication;
37 import org.tmatesoft.svn.core.wc.SVNClientManager;
38 import org.w3c.dom.Element;
39
40 import edu.internet2.middleware.shibboleth.common.resource.SVNBasicAuthenticationManager;
41 import edu.internet2.middleware.shibboleth.common.resource.SVNResource;
42
43
44 public class SVNResourceBeanDefinitionParser extends AbstractResourceBeanDefinitionParser {
45
46
47 public static final QName SCHEMA_TYPE = new QName(ResourceNamespaceHandler.NAMESPACE, "SVNResource");
48
49
50 public static final String REPOSITORY_URL_ATTRIB_NAME = "repositoryURL";
51
52
53 public static final String CTX_TIMEOUT_ATTRIB_NAME = "connectionTimeout";
54
55
56 public static final String READ_TIMEOUT_ATTRIB_NAME = "readTimeout";
57
58
59 public static final String WORKING_COPY_DIR_ATTRIB_NAME = "workingCopyDirectory";
60
61
62 public static final String REVISION_ATTRIB_NAME = "revision";
63
64
65
66
67
68 public static final String RESOURCE_FILE_ATTRIB_NAME = "resourceFile";
69
70
71 public static final String USERNAME_ATTRIB_NAME = "username";
72
73
74 public static final String PASSWORD_ATTRIB_NAME = "password";
75
76
77
78
79
80 public static final String PROXY_HOST_ATTRIB_NAME = "proxyHost";
81
82
83
84
85
86 public static final String PROXY_PORT_ATTRIB_NAME = "proxyPort";
87
88
89
90
91
92 public static final String PROXY_USERNAME_ATTRIB_NAME = "proxyUsername";
93
94
95
96
97
98 public static final String PROXY_PASSWORD_ATTRIB_NAME = "proxyPassword";
99
100
101 public static final int DEFAULT_CTX_TIMEOUT = 3000;
102
103
104 public static final int DEFAULT_READ_TIMEOUT = 5000;
105
106
107 public static final int DEFAULT_PROXY_PORT = 8080;
108
109
110 private final Logger log = LoggerFactory.getLogger(SVNResourceBeanDefinitionParser.class);
111
112
113 protected Class getBeanClass(Element arg0) {
114 return SVNResource.class;
115 }
116
117
118 protected String resolveId(Element configElement, AbstractBeanDefinition beanDefinition, ParserContext parserContext) {
119 return SVNResource.class.getName() + ":"
120 + DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null, REPOSITORY_URL_ATTRIB_NAME));
121 }
122
123
124 protected void doParse(Element configElement, ParserContext parserContext, BeanDefinitionBuilder builder)
125 throws BeanCreationException {
126 super.doParse(configElement, parserContext, builder);
127
128 builder.addConstructorArgValue(buildClientManager(configElement));
129
130 builder.addConstructorArgValue(getRespositoryUrl(configElement));
131
132 builder.addConstructorArgValue(getWorkingCopyDirectory(configElement));
133
134 builder.addConstructorArgValue(getRevision(configElement));
135
136 builder.addConstructorArgValue(getResourceFile(configElement));
137
138 addResourceFilter(configElement, parserContext, builder);
139 }
140
141
142
143
144
145
146
147
148 protected SVNClientManager buildClientManager(Element configElement) {
149 ArrayList<SVNAuthentication> authnMethods = new ArrayList<SVNAuthentication>();
150 String username = getUsername(configElement);
151 if (username != null) {
152 authnMethods.add(new SVNUserNameAuthentication(username, false));
153
154 String password = getPassword(configElement);
155 if (password != null) {
156 authnMethods.add(new SVNPasswordAuthentication(username, password, false));
157 }
158 }
159
160 String proxyHost = getProxyHost(configElement);
161 int proxyPort = getProxyPort(configElement);
162 String proxyUser = getProxyUsername(configElement);
163 String proxyPassword = getPassword(configElement);
164
165 SVNBasicAuthenticationManager authnManager;
166 if (proxyHost == null) {
167 authnManager = new SVNBasicAuthenticationManager(authnMethods);
168 } else {
169 authnManager = new SVNBasicAuthenticationManager(authnMethods, proxyHost, proxyPort, proxyUser,
170 proxyPassword);
171 }
172 authnManager.setConnectionTimeout(getConnectionTimeout(configElement));
173 authnManager.setReadTimeout(getReadTimeout(configElement));
174
175 SVNClientManager clientManager = SVNClientManager.newInstance();
176 clientManager.setAuthenticationManager(authnManager);
177 return clientManager;
178 }
179
180
181
182
183
184
185
186
187
188
189 protected SVNURL getRespositoryUrl(Element configElement) throws BeanCreationException {
190 if (!configElement.hasAttributeNS(null, REPOSITORY_URL_ATTRIB_NAME)) {
191 log.error("SVN resource definition missing required '" + REPOSITORY_URL_ATTRIB_NAME + "' attribute");
192 throw new BeanCreationException("SVN resource definition missing required '" + REPOSITORY_URL_ATTRIB_NAME
193 + "' attribute");
194 }
195
196 String repositoryUrl = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
197 REPOSITORY_URL_ATTRIB_NAME));
198 try {
199 return SVNURL.parseURIDecoded(repositoryUrl);
200 } catch (SVNException e) {
201 log.error("SVN remote repository URL " + repositoryUrl + " is not valid", e);
202 throw new BeanCreationException("SVN remote repository URL " + repositoryUrl + " is not valid", e);
203 }
204 }
205
206
207
208
209
210
211
212
213
214
215 protected int getConnectionTimeout(Element configElement) throws BeanCreationException {
216 if (!configElement.hasAttributeNS(null, CTX_TIMEOUT_ATTRIB_NAME)) {
217 return DEFAULT_CTX_TIMEOUT;
218 }
219
220 String timeout = DatatypeHelper.safeTrimOrNullString(configElement
221 .getAttributeNS(null, CTX_TIMEOUT_ATTRIB_NAME));
222 if (timeout == null) {
223 log.error("SVN resource definition attribute '" + CTX_TIMEOUT_ATTRIB_NAME + "' may not be an empty string");
224 throw new BeanCreationException("SVN resource definition attribute '" + CTX_TIMEOUT_ATTRIB_NAME
225 + "' may not be an empty string");
226 }
227
228 try {
229 return (int) XMLHelper.durationToLong(timeout);
230 } catch (IllegalArgumentException e) {
231 log.error("SVN resource definition attribute '" + CTX_TIMEOUT_ATTRIB_NAME
232 + "' does not contain a proper XML duration value");
233 throw new BeanCreationException("SVN resource definition attribute '" + CTX_TIMEOUT_ATTRIB_NAME
234 + "' does not contain a proper XML duration value");
235 }
236 }
237
238
239
240
241
242
243
244
245
246
247 protected int getReadTimeout(Element configElement) throws BeanCreationException {
248 if (!configElement.hasAttributeNS(null, READ_TIMEOUT_ATTRIB_NAME)) {
249 return DEFAULT_READ_TIMEOUT;
250 }
251
252 String timeout = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
253 READ_TIMEOUT_ATTRIB_NAME));
254 if (timeout == null) {
255 log
256 .error("SVN resource definition attribute '" + READ_TIMEOUT_ATTRIB_NAME
257 + "' may not be an empty string");
258 throw new BeanCreationException("SVN resource definition attribute '" + READ_TIMEOUT_ATTRIB_NAME
259 + "' may not be an empty string");
260 }
261
262 try {
263 return (int) XMLHelper.durationToLong(timeout);
264 } catch (IllegalArgumentException e) {
265 log.error("SVN resource definition attribute '" + READ_TIMEOUT_ATTRIB_NAME
266 + "' does not contain a proper XML duration value");
267 throw new BeanCreationException("SVN resource definition attribute '" + READ_TIMEOUT_ATTRIB_NAME
268 + "' does not contain a proper XML duration value");
269 }
270 }
271
272
273
274
275
276
277
278
279
280
281 protected File getWorkingCopyDirectory(Element configElement) throws BeanCreationException {
282 if (!configElement.hasAttributeNS(null, WORKING_COPY_DIR_ATTRIB_NAME)) {
283 log.error("SVN resource definition missing required '" + WORKING_COPY_DIR_ATTRIB_NAME + "' attribute");
284 throw new BeanCreationException("SVN resource definition missing required '" + WORKING_COPY_DIR_ATTRIB_NAME
285 + "' attribute");
286 }
287
288 File directory = new File(DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
289 WORKING_COPY_DIR_ATTRIB_NAME)));
290 if (directory == null) {
291 log.error("SVN working copy directory may not be null");
292 throw new BeanCreationException("SVN working copy directory may not be null");
293 }
294
295 if (!directory.exists()) {
296 boolean created = directory.mkdirs();
297 if (!created) {
298 log.error("SVN working copy direction " + directory.getAbsolutePath()
299 + " does not exist and could not be created");
300 throw new BeanCreationException("SVN working copy direction " + directory.getAbsolutePath()
301 + " does not exist and could not be created");
302 }
303 }
304
305 if (!directory.isDirectory()) {
306 log.error("SVN working copy location " + directory.getAbsolutePath() + " is not a directory");
307 throw new BeanCreationException("SVN working copy location " + directory.getAbsolutePath()
308 + " is not a directory");
309 }
310
311 if (!directory.canRead()) {
312 log.error("SVN working copy directory " + directory.getAbsolutePath() + " can not be read by this process");
313 throw new BeanCreationException("SVN working copy directory " + directory.getAbsolutePath()
314 + " can not be read by this process");
315 }
316
317 if (!directory.canWrite()) {
318 log.error("SVN working copy directory " + directory.getAbsolutePath()
319 + " can not be written to by this process");
320 throw new BeanCreationException("SVN working copy directory " + directory.getAbsolutePath()
321 + " can not be written to by this process");
322 }
323
324 return directory;
325 }
326
327
328
329
330
331
332
333
334
335
336 protected long getRevision(Element configElement) throws BeanCreationException {
337 if (!configElement.hasAttributeNS(null, REVISION_ATTRIB_NAME)) {
338 return -1;
339 }else{
340 try {
341 return Long.parseLong(DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
342 WORKING_COPY_DIR_ATTRIB_NAME)));
343 } catch (NumberFormatException e) {
344 log.error("SVN resource definition attribute '" + REVISION_ATTRIB_NAME + "' contains an invalid number");
345 throw new BeanCreationException("SVN resource definition attribute '" + REVISION_ATTRIB_NAME
346 + "' contains an invalid number");
347 }
348 }
349 }
350
351
352
353
354
355
356
357
358
359
360 protected String getResourceFile(Element configElement) throws BeanCreationException {
361 if (!configElement.hasAttributeNS(null, RESOURCE_FILE_ATTRIB_NAME)) {
362 log.error("SVN resource definition missing required '" + RESOURCE_FILE_ATTRIB_NAME + "' attribute");
363 throw new BeanCreationException("SVN resource definition missing required '" + RESOURCE_FILE_ATTRIB_NAME
364 + "' attribute");
365 }
366
367 String filename = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
368 RESOURCE_FILE_ATTRIB_NAME));
369 if (filename == null) {
370 log.error("SVN resource definition attribute '" + RESOURCE_FILE_ATTRIB_NAME
371 + "' may not be an empty string");
372 throw new BeanCreationException("SVN resource definition attribute '" + RESOURCE_FILE_ATTRIB_NAME
373 + "' may not be an empty string");
374 }
375
376 return filename;
377 }
378
379
380
381
382
383
384
385
386
387
388 protected String getUsername(Element configElement) throws BeanCreationException {
389 if (configElement.hasAttributeNS(null, USERNAME_ATTRIB_NAME)) {
390 String username = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
391 USERNAME_ATTRIB_NAME));
392 if (username == null) {
393 log
394 .error("SVN resource definition attribute '" + USERNAME_ATTRIB_NAME
395 + "' may not be an empty string");
396 throw new BeanCreationException("SVN resource definition attribute '" + USERNAME_ATTRIB_NAME
397 + "' may not be an empty string");
398 }
399 return username;
400 }
401
402 return null;
403 }
404
405
406
407
408
409
410
411
412
413
414 protected String getPassword(Element configElement) throws BeanCreationException {
415 if (configElement.hasAttributeNS(null, PASSWORD_ATTRIB_NAME)) {
416 String password = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
417 PASSWORD_ATTRIB_NAME));
418 if (password == null) {
419 log.error("SVN resource definition attribute '" + PASSWORD_ATTRIB_NAME
420 + "' may not be an empty string");
421 throw new BeanCreationException("SVN resource definition attribute '" + PASSWORD_ATTRIB_NAME
422 + "' may not be an empty string");
423 }
424 return password;
425 }
426 return null;
427 }
428
429
430
431
432
433
434
435
436
437
438 protected String getProxyHost(Element configElement) throws BeanCreationException {
439 if (configElement.hasAttributeNS(null, PROXY_HOST_ATTRIB_NAME)) {
440 String host = DatatypeHelper.safeTrimOrNullString(configElement
441 .getAttributeNS(null, PROXY_HOST_ATTRIB_NAME));
442 if (host == null) {
443 log.error("SVN resource definition attribute '" + PROXY_HOST_ATTRIB_NAME
444 + "' may not be an empty string");
445 throw new BeanCreationException("SVN resource definition attribute '" + PROXY_HOST_ATTRIB_NAME
446 + "' may not be an empty string");
447 }
448 return host;
449 }
450
451 return null;
452 }
453
454
455
456
457
458
459
460
461
462
463 protected int getProxyPort(Element configElement) throws BeanCreationException {
464 if (!configElement.hasAttributeNS(null, PROXY_PORT_ATTRIB_NAME)) {
465 return DEFAULT_PROXY_PORT;
466 }
467
468 String port = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null, PROXY_PORT_ATTRIB_NAME));
469 if (port == null) {
470 log.error("SVN resource definition attribute '" + PROXY_PORT_ATTRIB_NAME + "' may not be an empty string");
471 throw new BeanCreationException("SVN resource definition attribute '" + PROXY_PORT_ATTRIB_NAME
472 + "' may not be an empty string");
473 }
474
475 try {
476 return Integer.parseInt(port);
477 } catch (NumberFormatException e) {
478 log.error("SVN resource definition attribute '" + PROXY_PORT_ATTRIB_NAME + "' contains an invalid number");
479 throw new BeanCreationException("SVN resource definition attribute '" + PROXY_PORT_ATTRIB_NAME
480 + "' contains an invalid number");
481 }
482 }
483
484
485
486
487
488
489
490
491
492
493 protected String getProxyUsername(Element configElement) throws BeanCreationException {
494 if (configElement.hasAttributeNS(null, PROXY_USERNAME_ATTRIB_NAME)) {
495 String username = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
496 PROXY_USERNAME_ATTRIB_NAME));
497 if (username == null) {
498 log.error("SVN resource definition attribute '" + PROXY_USERNAME_ATTRIB_NAME
499 + "' may not be an empty string");
500 throw new BeanCreationException("SVN resource definition attribute '" + PROXY_USERNAME_ATTRIB_NAME
501 + "' may not be an empty string");
502 }
503 return username;
504 }
505 return null;
506 }
507
508
509
510
511
512
513
514
515
516
517 protected String getProxyPassword(Element configElement) throws BeanCreationException {
518 if (configElement.hasAttributeNS(null, PROXY_PASSWORD_ATTRIB_NAME)) {
519 String password = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
520 PROXY_PASSWORD_ATTRIB_NAME));
521 if (password == null) {
522 log.error("SVN resource definition attribute '" + PROXY_PASSWORD_ATTRIB_NAME
523 + "' may not be an empty string");
524 throw new BeanCreationException("SVN resource definition attribute '" + PROXY_PASSWORD_ATTRIB_NAME
525 + "' may not be an empty string");
526 }
527 return password;
528 }
529 return null;
530 }
531
532 }