View Javadoc

1   /*
2    * Copyright 2009 University Corporation for Advanced Internet Development, Inc.
3    *
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
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
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.
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  /** Bean definition parser for {@link SVNResource}s. */
44  public class SVNResourceBeanDefinitionParser extends AbstractResourceBeanDefinitionParser {
45  
46      /** Schema type. */
47      public static final QName SCHEMA_TYPE = new QName(ResourceNamespaceHandler.NAMESPACE, "SVNResource");
48  
49      /** Configuration element attribute {@value} which holds the URL to the remote repository. */
50      public static final String REPOSITORY_URL_ATTRIB_NAME = "repositoryURL";
51  
52      /** Configuration element attribute {@value} which holds the timeout used when connecting to the SVN server. */
53      public static final String CTX_TIMEOUT_ATTRIB_NAME = "connectionTimeout";
54  
55      /** Configuration element attribute {@value} which holds the timeout used when reading from the SVN server. */
56      public static final String READ_TIMEOUT_ATTRIB_NAME = "readTimeout";
57  
58      /** Configuration element attribute {@value} which holds the path to the working copy directory. */
59      public static final String WORKING_COPY_DIR_ATTRIB_NAME = "workingCopyDirectory";
60  
61      /** Configuration element attribute {@value} which holds the path to the working copy directory. */
62      public static final String REVISION_ATTRIB_NAME = "revision";
63  
64      /**
65       * Configuration element attribute {@value} which holds the path to the resource file represented by the SVN
66       * resource.
67       */
68      public static final String RESOURCE_FILE_ATTRIB_NAME = "resourceFile";
69  
70      /** Configuration element attribute {@value} which holds the SVN username. */
71      public static final String USERNAME_ATTRIB_NAME = "username";
72  
73      /** Configuration element attribute {@value} which holds the SVN password. */
74      public static final String PASSWORD_ATTRIB_NAME = "password";
75  
76      /**
77       * Configuration element attribute {@value} which holds the hostname of the proxy server used when connecting to the
78       * SVN server.
79       */
80      public static final String PROXY_HOST_ATTRIB_NAME = "proxyHost";
81  
82      /**
83       * Configuration element attribute {@value} which holds the port of the proxy server used when connecting to the SVN
84       * server.
85       */
86      public static final String PROXY_PORT_ATTRIB_NAME = "proxyPort";
87  
88      /**
89       * Configuration element attribute {@value} which holds the username used with the proxy server used when connecting
90       * to the SVN server.
91       */
92      public static final String PROXY_USERNAME_ATTRIB_NAME = "proxyUsername";
93  
94      /**
95       * Configuration element attribute {@value} which holds the password used with the proxy server used when connecting
96       * to the SVN server.
97       */
98      public static final String PROXY_PASSWORD_ATTRIB_NAME = "proxyPassword";
99  
100     /** Default value of {@value #CTX_TIMEOUT_ATTRIB_NAME}, {@value} milliseconds. */
101     public static final int DEFAULT_CTX_TIMEOUT = 3000;
102 
103     /** Default value of {@value #READ_TIMEOUT_ATTRIB_NAME}, {@value} milliseconds. */
104     public static final int DEFAULT_READ_TIMEOUT = 5000;
105 
106     /** Default value of {@value #PROXY_PORT_ATTRIB_NAME}, {@value} . */
107     public static final int DEFAULT_PROXY_PORT = 8080;
108 
109     /** Class logger. */
110     private final Logger log = LoggerFactory.getLogger(SVNResourceBeanDefinitionParser.class);
111 
112     /** {@inheritDoc} */
113     protected Class getBeanClass(Element arg0) {
114         return SVNResource.class;
115     }
116 
117     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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      * Builds the SVN client manager from the given configuration options.
143      * 
144      * @param configElement element bearing the configuration options
145      * 
146      * @return the SVN client manager
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      * Gets the value of the {@value #REPOSITORY_URL_ATTRIB_NAME} attribute.
182      * 
183      * @param configElement resource configuration element
184      * 
185      * @return value of the attribute
186      * 
187      * @throws BeanCreationException thrown if the attribute is missing or contains an invalid SVN URL
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      * Gets the value of the {@value #CTX_TIMEOUT_ATTRIB_NAME} attribute.
208      * 
209      * @param configElement resource configuration element
210      * 
211      * @return value of the attribute, or {@value #DEFAULT_CTX_TIMEOUT} if the attribute is not defined
212      * 
213      * @throws BeanCreationException thrown if the attribute is present but contains an empty string
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      * Gets the value of the {@value #READ_TIMEOUT_ATTRIB_NAME} attribute.
240      * 
241      * @param configElement resource configuration element
242      * 
243      * @return value of the attribute, or {@value #DEFAULT_READ_TIMEOUT} if the attribute is not defined
244      * 
245      * @throws BeanCreationException thrown if the attribute is present but contains an empty string
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      * Gets the value of the {@value #REPOSITORY_URL_ATTRIB_NAME} attribute.
274      * 
275      * @param configElement resource configuration element
276      * 
277      * @return value of the attribute
278      * 
279      * @throws BeanCreationException thrown if the attribute is missing or contains an invalid directory path
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      * Gets the value of the {@value #REVISION_ATTRIB_NAME} attribute.
329      * 
330      * @param configElement resource configuration element
331      * 
332      * @return value of the attribute
333      * 
334      * @throws BeanCreationException thrown if the attribute is missing or contains an invalid number
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      * Gets the value of the {@value #RESOURCE_FILE_ATTRIB_NAME} attribute.
353      * 
354      * @param configElement resource configuration element
355      * 
356      * @return value of the attribute
357      * 
358      * @throws BeanCreationException thrown if the attribute is missing or contains an empty string
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      * Gets the value of the {@value #USERNAME_ATTRIB_NAME} attribute.
381      * 
382      * @param configElement resource configuration element
383      * 
384      * @return value of the attribute
385      * 
386      * @throws BeanCreationException thrown if the attribute is present but contains an empty string
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      * Gets the value of the {@value #PASSWORD_ATTRIB_NAME} attribute.
407      * 
408      * @param configElement resource configuration element
409      * 
410      * @return value of the attribute
411      * 
412      * @throws BeanCreationException thrown if the attribute is present but contains an empty string
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      * Gets the value of the {@value #PROXY_HOST_ATTRIB_NAME} attribute.
431      * 
432      * @param configElement resource configuration element
433      * 
434      * @return value of the attribute
435      * 
436      * @throws BeanCreationException thrown if the attribute is present but contains an empty string
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      * Gets the value of the {@value #PROXY_PORT_ATTRIB_NAME} attribute.
456      * 
457      * @param configElement resource configuration element
458      * 
459      * @return value of the attribute, or {@value #DEFAULT_PROXY_PORT} if the attribute is not defined
460      * 
461      * @throws BeanCreationException thrown if the attribute is present but contains an empty string
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      * Gets the value of the {@value #PROXY_USERNAME_ATTRIB_NAME} attribute.
486      * 
487      * @param configElement resource configuration element
488      * 
489      * @return value of the attribute
490      * 
491      * @throws BeanCreationException thrown if the attribute is present but contains an empty string
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      * Gets the value of the {@value #PROXY_PASSWORD_ATTRIB_NAME} attribute.
510      * 
511      * @param configElement resource configuration element
512      * 
513      * @return value of the attribute
514      * 
515      * @throws BeanCreationException thrown if the attribute is present but contains an empty string
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 }