View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs.impl;
18  
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.apache.commons.vfs.FileSystemException;
22  import org.apache.commons.vfs.VfsLog;
23  import org.apache.commons.vfs.operations.FileOperationProvider;
24  import org.apache.commons.vfs.provider.FileProvider;
25  import org.apache.commons.vfs.util.Messages;
26  import org.w3c.dom.Element;
27  import org.w3c.dom.NodeList;
28  
29  import javax.xml.parsers.DocumentBuilder;
30  import javax.xml.parsers.DocumentBuilderFactory;
31  import javax.xml.parsers.ParserConfigurationException;
32  import java.io.File;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.net.MalformedURLException;
36  import java.net.URL;
37  import java.util.ArrayList;
38  import java.util.StringTokenizer;
39  import java.util.Enumeration;
40  import java.util.jar.JarEntry;
41  import java.util.jar.JarFile;
42  
43  /***
44   * A {@link org.apache.commons.vfs.FileSystemManager} that configures itself
45   * from an XML (Default: providers.xml) configuration file.<br>
46   * Certain providers are only loaded and available if the dependend library is in your
47   * classpath. You have to configure your debugging facility to log "debug" messages to see
48   * if a provider was skipped due to "unresolved externals".
49   *
50   * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
51   * @version $Revision: 480428 $ $Date: 2006-11-29 07:15:24 +0100 (Mi, 29 Nov 2006) $
52   */
53  public class StandardFileSystemManager
54      extends DefaultFileSystemManager
55  {
56      private Log log = LogFactory.getLog(StandardFileSystemManager.class);
57  
58      private static final String CONFIG_RESOURCE = "providers.xml";
59      private static final String PLUGIN_CONFIG_RESOURCE = "META-INF/vfs-providers.xml";
60  
61      private URL configUri;
62      private ClassLoader classLoader;
63  
64      /***
65       * Sets the configuration file for this manager.
66       */
67      public void setConfiguration(final String configUri)
68      {
69          try
70          {
71              setConfiguration(new URL(configUri));
72          }
73          catch (MalformedURLException e)
74          {
75              log.warn(e.getLocalizedMessage(), e);
76          }
77      }
78  
79      /***
80       * Sets the configuration file for this manager.
81       */
82      public void setConfiguration(final URL configUri)
83      {
84          this.configUri = configUri;
85      }
86  
87      /***
88       * Sets the ClassLoader to use to load the providers.  Default is to
89       * use the ClassLoader that loaded this class.
90       */
91      public void setClassLoader(final ClassLoader classLoader)
92      {
93          this.classLoader = classLoader;
94      }
95  
96      /***
97       * Initializes this manager.  Adds the providers and replicator.
98       */
99      public void init() throws FileSystemException
100     {
101         // Set the replicator and temporary file store (use the same component)
102         final DefaultFileReplicator replicator = createDefaultFileReplicator();
103         setReplicator(new PrivilegedFileReplicator(replicator));
104         setTemporaryFileStore(replicator);
105 
106         if (classLoader == null)
107         {
108             // Use default classloader
109             classLoader = getClass().getClassLoader();
110         }
111         if (configUri == null)
112         {
113             // Use default config
114             final URL url = getClass().getResource(CONFIG_RESOURCE);
115             if (url == null)
116             {
117                 throw new FileSystemException("vfs.impl/find-config-file.error", CONFIG_RESOURCE);
118             }
119             configUri = url;
120         }
121 
122         // Configure
123         configure(configUri);
124 
125         // Configure Plugins
126         configurePlugins();
127 
128         // Initialise super-class
129         super.init();
130     }
131 
132     /***
133      * Scans the classpath to find any droped plugin.<br />
134      * The plugin-description has to be in /META-INF/vfs-providers.xml
135      */
136     protected void configurePlugins() throws FileSystemException
137     {
138 		ClassLoader cl = findClassLoader();
139 
140 		Enumeration enumResources = null;
141 		try
142 		{
143 			enumResources = cl.getResources(PLUGIN_CONFIG_RESOURCE);
144 		}
145 		catch (IOException e)
146 		{
147 			throw new FileSystemException(e);
148 		}
149 		
150 		while (enumResources.hasMoreElements())
151 		{
152 			URL url = (URL) enumResources.nextElement();
153 			configure(url);
154 		}
155 	}
156 
157 	private ClassLoader findClassLoader()
158 	{
159 		ClassLoader cl = Thread.currentThread().getContextClassLoader();
160 		if (cl == null)
161 		{
162 			cl = getClass().getClassLoader();
163 		}
164 
165 		return cl;
166 	}
167 
168 	protected DefaultFileReplicator createDefaultFileReplicator()
169     {
170         return new DefaultFileReplicator();
171     }
172 
173     /***
174      * Configures this manager from an XML configuration file.
175      */
176     private void configure(final URL configUri) throws FileSystemException
177     {
178         InputStream configStream = null;
179         try
180         {
181             // Load up the config
182             // TODO - validate
183             final DocumentBuilder builder = createDocumentBuilder();
184             configStream = configUri.openStream();
185             final Element config = builder.parse(configStream).getDocumentElement();
186 
187             configure(config);
188         }
189         catch (final Exception e)
190         {
191             throw new FileSystemException("vfs.impl/load-config.error", configUri.toString(), e);
192         }
193         finally
194         {
195             if (configStream != null)
196             {
197                 try
198                 {
199                     configStream.close();
200                 }
201                 catch (IOException e)
202                 {
203                     log.warn(e.getLocalizedMessage(), e);
204                 }
205             }
206         }
207     }
208 
209     /***
210      * Configures this manager from an XML configuration file.
211      */
212     private void configure(final String configUri, final InputStream configStream) throws FileSystemException
213     {
214         try
215         {
216             // Load up the config
217             // TODO - validate
218             final DocumentBuilder builder = createDocumentBuilder();
219             final Element config = builder.parse(configStream).getDocumentElement();
220 
221             configure(config);
222 
223         }
224         catch (final Exception e)
225         {
226             throw new FileSystemException("vfs.impl/load-config.error", configUri, e);
227         }
228     }
229 
230     /***
231      * Configure and create a DocumentBuilder
232      */
233     private DocumentBuilder createDocumentBuilder() throws ParserConfigurationException
234     {
235         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
236         factory.setIgnoringElementContentWhitespace(true);
237         factory.setIgnoringComments(true);
238         factory.setExpandEntityReferences(true);
239         final DocumentBuilder builder = factory.newDocumentBuilder();
240         return builder;
241     }
242 
243     /***
244      * Configures this manager from an parsed XML configuration file
245      */
246     private void configure(final Element config) throws FileSystemException
247     {
248         // Add the providers
249         final NodeList providers = config.getElementsByTagName("provider");
250         final int count = providers.getLength();
251         for (int i = 0; i < count; i++)
252         {
253             final Element provider = (Element) providers.item(i);
254             addProvider(provider, false);
255         }
256 
257         // Add the operation providers
258         final NodeList operationProviders = config.getElementsByTagName("operationProvider");
259         for (int i = 0; i < operationProviders.getLength(); i++)
260         {
261             final Element operationProvider = (Element) operationProviders.item(i);
262             addOperationProvider(operationProvider);
263         }
264         
265         // Add the default provider
266         final NodeList defProviders = config.getElementsByTagName("default-provider");
267         if (defProviders.getLength() > 0)
268         {
269             final Element provider = (Element) defProviders.item(0);
270             addProvider(provider, true);
271         }
272 
273         // Add the mime-type maps
274         final NodeList mimeTypes = config.getElementsByTagName("mime-type-map");
275         for (int i = 0; i < mimeTypes.getLength(); i++)
276         {
277             final Element map = (Element) mimeTypes.item(i);
278             addMimeTypeMap(map);
279         }
280 
281         // Add the extension maps
282         final NodeList extensions = config.getElementsByTagName("extension-map");
283         for (int i = 0; i < extensions.getLength(); i++)
284         {
285             final Element map = (Element) extensions.item(i);
286             addExtensionMap(map);
287         }
288     }
289 
290     /***
291      * Adds an extension map.
292      */
293     private void addExtensionMap(final Element map)
294     {
295         final String extension = map.getAttribute("extension");
296         final String scheme = map.getAttribute("scheme");
297         if (scheme != null && scheme.length() > 0)
298         {
299             addExtensionMap(extension, scheme);
300         }
301     }
302 
303     /***
304      * Adds a mime-type map.
305      */
306     private void addMimeTypeMap(final Element map)
307     {
308         final String mimeType = map.getAttribute("mime-type");
309         final String scheme = map.getAttribute("scheme");
310         addMimeTypeMap(mimeType, scheme);
311     }
312 
313     /***
314      * Adds a provider from a provider definition.
315      */
316     private void addProvider(final Element providerDef, final boolean isDefault)
317         throws FileSystemException
318     {
319         final String classname = providerDef.getAttribute("class-name");
320 
321         // Make sure all required schemes are available
322         final String[] requiredSchemes = getRequiredSchemes(providerDef);
323         for (int i = 0; i < requiredSchemes.length; i++)
324         {
325             final String requiredScheme = requiredSchemes[i];
326             if (!hasProvider(requiredScheme))
327             {
328                 final String msg = Messages.getString("vfs.impl/skipping-provider-scheme.debug",
329                     new String[]{classname, requiredScheme});
330                 VfsLog.debug(getLogger(), log, msg);
331                 return;
332             }
333         }
334 
335         // Make sure all required classes are in classpath
336         final String[] requiredClasses = getRequiredClasses(providerDef);
337         for (int i = 0; i < requiredClasses.length; i++)
338         {
339             final String requiredClass = requiredClasses[i];
340             if (!findClass(requiredClass))
341             {
342                 final String msg = Messages.getString("vfs.impl/skipping-provider.debug",
343                     new String[]{classname, requiredClass});
344                 VfsLog.debug(getLogger(), log, msg);
345                 return;
346             }
347         }
348 
349         // Create and register the provider
350         final FileProvider provider = (FileProvider) createInstance(classname);
351         final String[] schemas = getSchemas(providerDef);
352         if (schemas.length > 0)
353         {
354             addProvider(schemas, provider);
355         }
356 
357         // Set as default, if required
358         if (isDefault)
359         {
360             setDefaultProvider(provider);
361         }
362     }
363 
364     /***
365      * Adds a operationProvider from a operationProvider definition.
366      */
367     private void addOperationProvider(final Element providerDef) throws FileSystemException
368     {
369         final String classname = providerDef.getAttribute("class-name");
370 
371         // Attach only to available schemas
372         final String[] schemas = getSchemas(providerDef);
373         for (int i = 0; i < schemas.length; i++)
374         {
375             final String schema = schemas[i];
376             if (hasProvider(schema))
377             {
378                 final FileOperationProvider operationProvider = (FileOperationProvider) createInstance(classname);
379             	addOperationProvider(schema, operationProvider);
380             }
381         }
382     }
383     
384     /***
385      * Tests if a class is available.
386      */
387     private boolean findClass(final String className)
388     {
389         try
390         {
391             classLoader.loadClass(className);
392             return true;
393         }
394         catch (final ClassNotFoundException e)
395         {
396             return false;
397         }
398     }
399 
400     /***
401      * Extracts the required classes from a provider definition.
402      */
403     private String[] getRequiredClasses(final Element providerDef)
404     {
405         final ArrayList classes = new ArrayList();
406         final NodeList deps = providerDef.getElementsByTagName("if-available");
407         final int count = deps.getLength();
408         for (int i = 0; i < count; i++)
409         {
410             final Element dep = (Element) deps.item(i);
411             String className = dep.getAttribute("class-name");
412             if (className != null && className.length() > 0)
413             {
414                 classes.add(className);
415             }
416         }
417         return (String[]) classes.toArray(new String[classes.size()]);
418     }
419 
420     /***
421      * Extracts the required schemes from a provider definition.
422      */
423     private String[] getRequiredSchemes(final Element providerDef)
424     {
425         final ArrayList schemes = new ArrayList();
426         final NodeList deps = providerDef.getElementsByTagName("if-available");
427         final int count = deps.getLength();
428         for (int i = 0; i < count; i++)
429         {
430             final Element dep = (Element) deps.item(i);
431             String scheme = dep.getAttribute("scheme");
432             if (scheme != null && scheme.length() > 0)
433             {
434                 schemes.add(scheme);
435             }
436         }
437         return (String[]) schemes.toArray(new String[schemes.size()]);
438     }
439 
440     /***
441      * Extracts the schema names from a provider definition.
442      */
443     private String[] getSchemas(final Element provider)
444     {
445         final ArrayList schemas = new ArrayList();
446         final NodeList schemaElements = provider.getElementsByTagName("scheme");
447         final int count = schemaElements.getLength();
448         for (int i = 0; i < count; i++)
449         {
450             final Element scheme = (Element) schemaElements.item(i);
451             schemas.add(scheme.getAttribute("name"));
452         }
453         return (String[]) schemas.toArray(new String[schemas.size()]);
454     }
455 
456     /***
457      * Creates a provider.
458      */
459     private Object createInstance(final String className)
460         throws FileSystemException
461     {
462         try
463         {
464             final Class clazz = classLoader.loadClass(className);
465             return clazz.newInstance();
466         }
467         catch (final Exception e)
468         {
469             throw new FileSystemException("vfs.impl/create-provider.error", className, e);
470         }
471     }
472 }