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 }