VFSFileSystem.java

  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.configuration2.io;

  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.OutputStream;
  22. import java.lang.reflect.Method;
  23. import java.net.MalformedURLException;
  24. import java.net.URL;
  25. import java.net.URLConnection;
  26. import java.net.URLStreamHandler;
  27. import java.util.Map;

  28. import org.apache.commons.configuration2.ex.ConfigurationException;
  29. import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
  30. import org.apache.commons.logging.Log;
  31. import org.apache.commons.logging.LogFactory;
  32. import org.apache.commons.vfs2.FileContent;
  33. import org.apache.commons.vfs2.FileName;
  34. import org.apache.commons.vfs2.FileObject;
  35. import org.apache.commons.vfs2.FileSystemConfigBuilder;
  36. import org.apache.commons.vfs2.FileSystemException;
  37. import org.apache.commons.vfs2.FileSystemManager;
  38. import org.apache.commons.vfs2.FileSystemOptions;
  39. import org.apache.commons.vfs2.VFS;
  40. import org.apache.commons.vfs2.provider.UriParser;

  41. /**
  42.  * FileSystem that uses <a href="https://commons.apache.org/proper/commons-vfs/">Apache Commons VFS</a>.
  43.  *
  44.  * @since 1.7
  45.  */
  46. public class VFSFileSystem extends DefaultFileSystem {

  47.     /**
  48.      * Stream handler required to create URL.
  49.      */
  50.     private static final class VFSURLStreamHandler extends URLStreamHandler {

  51.         @Override
  52.         protected URLConnection openConnection(final URL url) throws IOException {
  53.             throw new IOException("VFS URLs can only be used with VFS APIs");
  54.         }
  55.     }

  56.     /** The logger. */
  57.     private final Log log = LogFactory.getLog(getClass());

  58.     public VFSFileSystem() {
  59.         // empty
  60.     }

  61.     @Override
  62.     public String getBasePath(final String path) {
  63.         if (UriParser.extractScheme(path) == null) {
  64.             return super.getBasePath(path);
  65.         }
  66.         try {
  67.             final FileName parent = resolveURI(path).getParent();
  68.             return parent != null ? parent.getURI() : null;
  69.         } catch (final FileSystemException fse) {
  70.             fse.printStackTrace();
  71.             return null;
  72.         }
  73.     }

  74.     @Override
  75.     public String getFileName(final String path) {
  76.         if (UriParser.extractScheme(path) == null) {
  77.             return super.getFileName(path);
  78.         }
  79.         try {
  80.             return resolveURI(path).getBaseName();
  81.         } catch (final FileSystemException fse) {
  82.             fse.printStackTrace();
  83.             return null;
  84.         }
  85.     }

  86.     @Override
  87.     public InputStream getInputStream(final URL url) throws ConfigurationException {
  88.         final FileObject file;
  89.         try {
  90.             final FileSystemOptions opts = getOptions(url.getProtocol());
  91.             file = getManager().resolveFile(url.toString(), opts);
  92.             if (!file.exists()) {
  93.                 throw new ConfigurationException("File not found");
  94.             }
  95.             if (!file.isFile()) {
  96.                 throw new ConfigurationException("Cannot load a configuration from a directory");
  97.             }
  98.             final FileContent content = file.getContent();
  99.             if (content == null) {
  100.                 final String msg = "Cannot access content of " + file.getName().getFriendlyURI();
  101.                 throw new ConfigurationException(msg);
  102.             }
  103.             return content.getInputStream();
  104.         } catch (final FileSystemException fse) {
  105.             final String msg = "Unable to access " + url.toString();
  106.             throw new ConfigurationException(msg, fse);
  107.         }
  108.     }

  109.     private FileSystemManager getManager() throws FileSystemException {
  110.         return VFS.getManager();
  111.     }

  112.     private FileSystemOptions getOptions(final String scheme) {
  113.         if (scheme == null) {
  114.             return null;
  115.         }
  116.         final FileSystemOptions opts = new FileSystemOptions();
  117.         final FileSystemConfigBuilder builder;
  118.         try {
  119.             builder = getManager().getFileSystemConfigBuilder(scheme);
  120.         } catch (final Exception ex) {
  121.             return null;
  122.         }
  123.         final FileOptionsProvider provider = getFileOptionsProvider();
  124.         if (provider != null) {
  125.             final Map<String, Object> map = provider.getOptions();
  126.             if (map == null) {
  127.                 return null;
  128.             }
  129.             int count = 0;
  130.             for (final Map.Entry<String, Object> entry : map.entrySet()) {
  131.                 try {
  132.                     String key = entry.getKey();
  133.                     if (FileOptionsProvider.CURRENT_USER.equals(key)) {
  134.                         key = "creatorName";
  135.                     }
  136.                     setProperty(builder, opts, key, entry.getValue());
  137.                     ++count;
  138.                 } catch (final Exception ex) {
  139.                     // Ignore an incorrect property.
  140.                     continue;
  141.                 }
  142.             }
  143.             if (count > 0) {
  144.                 return opts;
  145.             }
  146.         }
  147.         return null;

  148.     }

  149.     @Override
  150.     public OutputStream getOutputStream(final URL url) throws ConfigurationException {
  151.         try {
  152.             final FileSystemOptions opts = getOptions(url.getProtocol());
  153.             final FileObject file = getManager().resolveFile(url.toString(), opts);
  154.             // throw an exception if the target URL is a directory
  155.             if (file == null || file.isFolder()) {
  156.                 throw new ConfigurationException("Cannot save a configuration to a directory");
  157.             }
  158.             final FileContent content = file.getContent();

  159.             if (content == null) {
  160.                 throw new ConfigurationException("Cannot access content of " + url);
  161.             }
  162.             return content.getOutputStream();
  163.         } catch (final FileSystemException fse) {
  164.             throw new ConfigurationException("Unable to access " + url, fse);
  165.         }
  166.     }

  167.     @Override
  168.     public String getPath(final File file, final URL url, final String basePath, final String fileName) {
  169.         if (file != null) {
  170.             return super.getPath(file, url, basePath, fileName);
  171.         }
  172.         try {
  173.             if (url != null) {
  174.                 final FileName name = resolveURI(url.toString());
  175.                 if (name != null) {
  176.                     return name.toString();
  177.                 }
  178.             }
  179.             if (UriParser.extractScheme(fileName) != null) {
  180.                 return fileName;
  181.             }
  182.             if (basePath != null) {
  183.                 final FileName base = resolveURI(basePath);
  184.                 return getManager().resolveName(base, fileName).getURI();
  185.             }
  186.             final FileName name = resolveURI(fileName);
  187.             final FileName base = name.getParent();
  188.             return getManager().resolveName(base, name.getBaseName()).getURI();
  189.         } catch (final FileSystemException fse) {
  190.             fse.printStackTrace();
  191.             return null;
  192.         }
  193.     }

  194.     @Override
  195.     public URL getURL(final String basePath, final String file) throws MalformedURLException {
  196.         if (basePath != null && UriParser.extractScheme(basePath) == null || basePath == null && UriParser.extractScheme(file) == null) {
  197.             return super.getURL(basePath, file);
  198.         }
  199.         try {
  200.             final FileName path;
  201.             if (basePath != null && UriParser.extractScheme(file) == null) {
  202.                 final FileName base = resolveURI(basePath);
  203.                 path = getManager().resolveName(base, file);
  204.             } else {
  205.                 path = resolveURI(file);
  206.             }

  207.             final URLStreamHandler handler = new VFSURLStreamHandler();
  208.             return new URL(null, path.getURI(), handler);
  209.         } catch (final FileSystemException fse) {
  210.             throw new ConfigurationRuntimeException("Could not parse basePath: " + basePath + " and fileName: " + file, fse);
  211.         }
  212.     }

  213.     @Override
  214.     public URL locateFromURL(final String basePath, final String fileName) {
  215.         final String fileScheme = UriParser.extractScheme(fileName);
  216.         // Use DefaultFileSystem if basePath and fileName don't have a scheme.
  217.         if ((basePath == null || UriParser.extractScheme(basePath) == null) && fileScheme == null) {
  218.             return super.locateFromURL(basePath, fileName);
  219.         }
  220.         try {
  221.             final FileObject file;
  222.             // Only use the base path if the file name doesn't have a scheme.
  223.             if (basePath != null && fileScheme == null) {
  224.                 final String scheme = UriParser.extractScheme(basePath);
  225.                 final FileSystemOptions opts = getOptions(scheme);
  226.                 FileObject base = getManager().resolveFile(basePath, opts);
  227.                 if (base.isFile()) {
  228.                     base = base.getParent();
  229.                 }

  230.                 file = getManager().resolveFile(base, fileName);
  231.             } else {
  232.                 final FileSystemOptions opts = getOptions(fileScheme);
  233.                 file = getManager().resolveFile(fileName, opts);
  234.             }

  235.             if (!file.exists()) {
  236.                 return null;
  237.             }
  238.             final FileName path = file.getName();
  239.             final URLStreamHandler handler = new VFSURLStreamHandler();
  240.             return new URL(null, path.getURI(), handler);
  241.         } catch (final FileSystemException | MalformedURLException fse) {
  242.             return null;
  243.         }
  244.     }

  245.     private FileName resolveURI(final String path) throws FileSystemException {
  246.         return getManager().resolveURI(path);
  247.     }

  248.     private void setProperty(final FileSystemConfigBuilder builder, final FileSystemOptions options, final String key, final Object value) {
  249.         final String methodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
  250.         final Class<?>[] paramTypes = new Class<?>[2];
  251.         paramTypes[0] = FileSystemOptions.class;
  252.         paramTypes[1] = value.getClass();
  253.         try {
  254.             final Method method = builder.getClass().getMethod(methodName, paramTypes);
  255.             final Object[] params = new Object[2];
  256.             params[0] = options;
  257.             params[1] = value;
  258.             method.invoke(builder, params);
  259.         } catch (final Exception ex) {
  260.             log.warn("Cannot access property '" + key + "'! Ignoring.", ex);
  261.         }
  262.     }
  263. }