    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.configuration2.io;
    018
    019import java.io.File;
    020import java.io.IOException;
    021import java.io.InputStream;
    022import java.io.OutputStream;
    023import java.lang.reflect.Method;
    024import java.net.MalformedURLException;
    025import java.net.URL;
    026import java.net.URLConnection;
    027import java.net.URLStreamHandler;
    028import java.util.Map;
    029
    030import org.apache.commons.configuration2.ex.ConfigurationException;
    031import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
    032import org.apache.commons.logging.Log;
    033import org.apache.commons.logging.LogFactory;
    034import org.apache.commons.vfs2.FileContent;
    035import org.apache.commons.vfs2.FileName;
    036import org.apache.commons.vfs2.FileObject;
    037import org.apache.commons.vfs2.FileSystemConfigBuilder;
    038import org.apache.commons.vfs2.FileSystemException;
    039import org.apache.commons.vfs2.FileSystemManager;
    040import org.apache.commons.vfs2.FileSystemOptions;
    041import org.apache.commons.vfs2.FileType;
    042import org.apache.commons.vfs2.VFS;
    043import org.apache.commons.vfs2.provider.UriParser;
    044
    045/**
    046 * FileSystem that uses Commons VFS
    047 * @since 1.7
    048 * @author <a
    049 * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
    050 */
    051public class VFSFileSystem extends DefaultFileSystem
    052{
    053    /** The logger. */
    054    private final Log log = LogFactory.getLog(getClass());
    055
    056    public VFSFileSystem()
    057    {
    058    }
    059
    060    @Override
    061    public InputStream getInputStream(final URL url) throws ConfigurationException
    062    {
    063        FileObject file;
    064        try
    065        {
    066            final FileSystemOptions opts = getOptions(url.getProtocol());
    067            file = (opts == null) ? VFS.getManager().resolveFile(url.toString())
    068                    : VFS.getManager().resolveFile(url.toString(), opts);
    069            if (file.getType() != FileType.FILE)
    070            {
    071                throw new ConfigurationException("Cannot load a configuration from a directory");
    072            }
    073            final FileContent content = file.getContent();
    074            if (content == null)
    075            {
    076                final String msg = "Cannot access content of " + file.getName().getFriendlyURI();
    077                throw new ConfigurationException(msg);
    078            }
    079            return content.getInputStream();
    080        }
    081        catch (final FileSystemException fse)
    082        {
    083            final String msg = "Unable to access " + url.toString();
    084            throw new ConfigurationException(msg, fse);
    085        }
    086    }
    087
    088    @Override
    089    public OutputStream getOutputStream(final URL url) throws ConfigurationException
    090    {
    091        try
    092        {
    093            final FileSystemOptions opts = getOptions(url.getProtocol());
    094            final FileSystemManager fsManager = VFS.getManager();
    095            final FileObject file = (opts == null) ? fsManager.resolveFile(url.toString())
    096                    : fsManager.resolveFile(url.toString(), opts);
    097            // throw an exception if the target URL is a directory
    098            if (file == null || file.getType() == FileType.FOLDER)
    099            {
    100                throw new ConfigurationException("Cannot save a configuration to a directory");
    101            }
    102            final FileContent content = file.getContent();
    103
    104            if (content == null)
    105            {
    106                throw new ConfigurationException("Cannot access content of " + url);
    107            }
    108            return content.getOutputStream();
    109        }
    110        catch (final FileSystemException fse)
    111        {
    112            throw new ConfigurationException("Unable to access " + url, fse);
    113        }
    114    }
    115
    116    @Override
    117    public String getPath(final File file, final URL url, final String basePath, final String fileName)
    118    {
    119        if (file != null)
    120        {
    121            return super.getPath(file, url, basePath, fileName);
    122        }
    123        try
    124        {
    125            final FileSystemManager fsManager = VFS.getManager();
    126            if (url != null)
    127            {
    128                final FileName name = fsManager.resolveURI(url.toString());
    129                if (name != null)
    130                {
    131                    return name.toString();
    132                }
    133            }
    134
    135            if (UriParser.extractScheme(fileName) != null)
    136            {
    137                return fileName;
    138            }
    139            else if (basePath != null)
    140            {
    141                final FileName base = fsManager.resolveURI(basePath);
    142                return fsManager.resolveName(base, fileName).getURI();
    143            }
    144            else
    145            {
    146                final FileName name = fsManager.resolveURI(fileName);
    147                final FileName base = name.getParent();
    148                return fsManager.resolveName(base, name.getBaseName()).getURI();
    149            }
    150        }
    151        catch (final FileSystemException fse)
    152        {
    153            fse.printStackTrace();
    154            return null;
    155        }
    156    }
    157
    158    @Override
    159    public String getBasePath(final String path)
    160    {
    161        if (UriParser.extractScheme(path) == null)
    162        {
    163            return super.getBasePath(path);
    164        }
    165        try
    166        {
    167            final FileSystemManager fsManager = VFS.getManager();
    168            final FileName name = fsManager.resolveURI(path);
    169            return name.getParent().getURI();
    170        }
    171        catch (final FileSystemException fse)
    172        {
    173            fse.printStackTrace();
    174            return null;
    175        }
    176    }
    177
    178    @Override
    179    public String getFileName(final String path)
    180    {
    181        if (UriParser.extractScheme(path) == null)
    182        {
    183            return super.getFileName(path);
    184        }
    185        try
    186        {
    187            final FileSystemManager fsManager = VFS.getManager();
    188            final FileName name = fsManager.resolveURI(path);
    189            return name.getBaseName();
    190        }
    191        catch (final FileSystemException fse)
    192        {
    193            fse.printStackTrace();
    194            return null;
    195        }
    196    }
    197
    198    @Override
    199    public URL getURL(final String basePath, final String file) throws MalformedURLException
    200    {
    201        if ((basePath != null && UriParser.extractScheme(basePath) == null)
    202            || (basePath == null && UriParser.extractScheme(file) == null))
    203        {
    204            return super.getURL(basePath, file);
    205        }
    206        try
    207        {
    208            final FileSystemManager fsManager = VFS.getManager();
    209
    210            FileName path;
    211            if (basePath != null && UriParser.extractScheme(file) == null)
    212            {
    213                final FileName base = fsManager.resolveURI(basePath);
    214                path = fsManager.resolveName(base, file);
    215            }
    216            else
    217            {
    218                path = fsManager.resolveURI(file);
    219            }
    220
    221            final URLStreamHandler handler = new VFSURLStreamHandler(path);
    222            return new URL(null, path.getURI(), handler);
    223        }
    224        catch (final FileSystemException fse)
    225        {
    226            throw new ConfigurationRuntimeException("Could not parse basePath: " + basePath
    227                + " and fileName: " + file, fse);
    228        }
    229    }
    230
    231    @Override
    232    public URL locateFromURL(final String basePath, final String fileName)
    233    {
    234        final String fileScheme = UriParser.extractScheme(fileName);
    235
    236        // Use DefaultFileSystem if basePath and fileName don't have a scheme.
    237        if ((basePath == null || UriParser.extractScheme(basePath) == null) && fileScheme == null)
    238        {
    239            return super.locateFromURL(basePath, fileName);
    240        }
    241        try
    242        {
    243            final FileSystemManager fsManager = VFS.getManager();
    244
    245            FileObject file;
    246            // Only use the base path if the file name doesn't have a scheme.
    247            if (basePath != null && fileScheme == null)
    248            {
    249                final String scheme = UriParser.extractScheme(basePath);
    250                final FileSystemOptions opts = (scheme != null) ? getOptions(scheme) : null;
    251                FileObject base = (opts == null) ? fsManager.resolveFile(basePath)
    252                        : fsManager.resolveFile(basePath, opts);
    253                if (base.getType() == FileType.FILE)
    254                {
    255                    base = base.getParent();
    256                }
    257
    258                file = fsManager.resolveFile(base, fileName);
    259            }
    260            else
    261            {
    262                final FileSystemOptions opts = (fileScheme != null) ? getOptions(fileScheme) : null;
    263                file = (opts == null) ? fsManager.resolveFile(fileName)
    264                        : fsManager.resolveFile(fileName, opts);
    265            }
    266
    267            if (!file.exists())
    268            {
    269                return null;
    270            }
    271            final FileName path = file.getName();
    272            final URLStreamHandler handler = new VFSURLStreamHandler(path);
    273            return new URL(null, path.getURI(), handler);
    274        }
    275        catch (final FileSystemException fse)
    276        {
    277            return null;
    278        }
    279        catch (final MalformedURLException ex)
    280        {
    281            return null;
    282        }
    283    }
    284
    285    private FileSystemOptions getOptions(final String scheme)
    286    {
    287        final FileSystemOptions opts = new FileSystemOptions();
    288        FileSystemConfigBuilder builder;
    289        try
    290        {
    291            builder = VFS.getManager().getFileSystemConfigBuilder(scheme);
    292        }
    293        catch (final Exception ex)
    294        {
    295            return null;
    296        }
    297        final FileOptionsProvider provider = getFileOptionsProvider();
    298        if (provider != null)
    299        {
    300            final Map<String, Object> map = provider.getOptions();
    301            if (map == null)
    302            {
    303                return null;
    304            }
    305            int count = 0;
    306            for (final Map.Entry<String, Object> entry : map.entrySet())
    307            {
    308                try
    309                {
    310                    String key = entry.getKey();
    311                    if (FileOptionsProvider.CURRENT_USER.equals(key))
    312                    {
    313                        key = "creatorName";
    314                    }
    315                    setProperty(builder, opts, key, entry.getValue());
    316                    ++count;
    317                }
    318                catch (final Exception ex)
    319                {
    320                    // Ignore an incorrect property.
    321                    continue;
    322                }
    323            }
    324            if (count > 0)
    325            {
    326                return opts;
    327            }
    328        }
    329        return null;
    330
    331    }
    332
    333    private void setProperty(final FileSystemConfigBuilder builder, final FileSystemOptions options,
    334                             final String key, final Object value)
    335    {
    336        final String methodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
    337        final Class<?>[] paramTypes = new Class<?>[2];
    338        paramTypes[0] = FileSystemOptions.class;
    339        paramTypes[1] = value.getClass();
    340
    341        try
    342        {
    343            final Method method = builder.getClass().getMethod(methodName, paramTypes);
    344            final Object[] params = new Object[2];
    345            params[0] = options;
    346            params[1] = value;
    347            method.invoke(builder, params);
    348        }
    349        catch (final Exception ex)
    350        {
    351            log.warn("Cannot access property '" + key + "'! Ignoring.", ex);
    352        }
    353
    354    }
    355
    356    /**
    357     * Stream handler required to create URL.
    358     */
    359    private static class VFSURLStreamHandler extends URLStreamHandler
    360    {
    361        /** The Protocol used */
    362        private final String protocol;
    363
    364        public VFSURLStreamHandler(final FileName file)
    365        {
    366            this.protocol = file.getScheme();
    367        }
    368
    369        @Override
    370        protected URLConnection openConnection(final URL url) throws IOException
    371        {
    372            throw new IOException("VFS URLs can only be used with VFS APIs");
    373        }
    374    }
    375}