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.configuration;
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.vfs2.FileContent;
031import org.apache.commons.vfs2.FileName;
032import org.apache.commons.vfs2.FileObject;
033import org.apache.commons.vfs2.FileSystemConfigBuilder;
034import org.apache.commons.vfs2.FileSystemException;
035import org.apache.commons.vfs2.FileSystemManager;
036import org.apache.commons.vfs2.FileSystemOptions;
037import org.apache.commons.vfs2.FileType;
038import org.apache.commons.vfs2.VFS;
039import org.apache.commons.vfs2.provider.UriParser;
040
041/**
042 * FileSystem that uses Commons VFS
043 * @since 1.7
044 * @author <a
045 * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
046 * @version $Id: VFSFileSystem.html 901729 2014-03-15 20:24:09Z oheger $
047 */
048public class VFSFileSystem extends DefaultFileSystem
049{
050    public VFSFileSystem()
051    {
052    }
053
054    @Override
055    public InputStream getInputStream(String basePath, String fileName)
056        throws ConfigurationException
057    {
058        try
059        {
060            FileSystemManager manager = VFS.getManager();
061            FileName path;
062            if (basePath != null)
063            {
064                FileName base = manager.resolveURI(basePath);
065                path = manager.resolveName(base, fileName);
066            }
067            else
068            {
069                FileName file = manager.resolveURI(fileName);
070                FileName base = file.getParent();
071                path = manager.resolveName(base, file.getBaseName());
072            }
073            FileSystemOptions opts = getOptions(path.getScheme());
074            FileObject file = (opts == null) ? manager.resolveFile(path.getURI())
075                    : manager.resolveFile(path.getURI(), opts);
076            FileContent content = file.getContent();
077            if (content == null)
078            {
079                String msg = "Cannot access content of " + file.getName().getFriendlyURI();
080                throw new ConfigurationException(msg);
081            }
082            return content.getInputStream();
083        }
084        catch (ConfigurationException e)
085        {
086            throw e;
087        }
088        catch (Exception e)
089        {
090            throw new ConfigurationException("Unable to load the configuration file " + fileName, e);
091        }
092    }
093
094    @Override
095    public InputStream getInputStream(URL url) throws ConfigurationException
096    {
097        FileObject file;
098        try
099        {
100            FileSystemOptions opts = getOptions(url.getProtocol());
101            file = (opts == null) ? VFS.getManager().resolveFile(url.toString())
102                    : VFS.getManager().resolveFile(url.toString(), opts);
103            if (file.getType() != FileType.FILE)
104            {
105                throw new ConfigurationException("Cannot load a configuration from a directory");
106            }
107            FileContent content = file.getContent();
108            if (content == null)
109            {
110                String msg = "Cannot access content of " + file.getName().getFriendlyURI();
111                throw new ConfigurationException(msg);
112            }
113            return content.getInputStream();
114        }
115        catch (FileSystemException fse)
116        {
117            String msg = "Unable to access " + url.toString();
118            throw new ConfigurationException(msg, fse);
119        }
120    }
121
122    @Override
123    public OutputStream getOutputStream(URL url) throws ConfigurationException
124    {
125        try
126        {
127            FileSystemOptions opts = getOptions(url.getProtocol());
128            FileSystemManager fsManager = VFS.getManager();
129            FileObject file = (opts == null) ? fsManager.resolveFile(url.toString())
130                    : fsManager.resolveFile(url.toString(), opts);
131            // throw an exception if the target URL is a directory
132            if (file == null || file.getType() == FileType.FOLDER)
133            {
134                throw new ConfigurationException("Cannot save a configuration to a directory");
135            }
136            FileContent content = file.getContent();
137
138            if (content == null)
139            {
140                throw new ConfigurationException("Cannot access content of " + url);
141            }
142            return content.getOutputStream();
143        }
144        catch (FileSystemException fse)
145        {
146            throw new ConfigurationException("Unable to access " + url, fse);
147        }
148    }
149
150    @Override
151    public String getPath(File file, URL url, String basePath, String fileName)
152    {
153        if (file != null)
154        {
155            return super.getPath(file, url, basePath, fileName);
156        }
157        try
158        {
159            FileSystemManager fsManager = VFS.getManager();
160            if (url != null)
161            {
162                FileName name = fsManager.resolveURI(url.toString());
163                if (name != null)
164                {
165                    return name.toString();
166                }
167            }
168
169            if (UriParser.extractScheme(fileName) != null)
170            {
171                return fileName;
172            }
173            else if (basePath != null)
174            {
175                FileName base = fsManager.resolveURI(basePath);
176                return fsManager.resolveName(base, fileName).getURI();
177            }
178            else
179            {
180                FileName name = fsManager.resolveURI(fileName);
181                FileName base = name.getParent();
182                return fsManager.resolveName(base, name.getBaseName()).getURI();
183            }
184        }
185        catch (FileSystemException fse)
186        {
187            fse.printStackTrace();
188            return null;
189        }
190    }
191
192    @Override
193    public String getBasePath(String path)
194    {
195        if (UriParser.extractScheme(path) == null)
196        {
197            return super.getBasePath(path);
198        }
199        try
200        {
201            FileSystemManager fsManager = VFS.getManager();
202            FileName name = fsManager.resolveURI(path);
203            return name.getParent().getURI();
204        }
205        catch (FileSystemException fse)
206        {
207            fse.printStackTrace();
208            return null;
209        }
210    }
211
212    @Override
213    public String getFileName(String path)
214    {
215        if (UriParser.extractScheme(path) == null)
216        {
217            return super.getFileName(path);
218        }
219        try
220        {
221            FileSystemManager fsManager = VFS.getManager();
222            FileName name = fsManager.resolveURI(path);
223            return name.getBaseName();
224        }
225        catch (FileSystemException fse)
226        {
227            fse.printStackTrace();
228            return null;
229        }
230    }
231
232    @Override
233    public URL getURL(String basePath, String file) throws MalformedURLException
234    {
235        if ((basePath != null && UriParser.extractScheme(basePath) == null)
236            || (basePath == null && UriParser.extractScheme(file) == null))
237        {
238            return super.getURL(basePath, file);
239        }
240        try
241        {
242            FileSystemManager fsManager = VFS.getManager();
243
244            FileName path;
245            if (basePath != null && UriParser.extractScheme(file) == null)
246            {
247                FileName base = fsManager.resolveURI(basePath);
248                path = fsManager.resolveName(base, file);
249            }
250            else
251            {
252                path = fsManager.resolveURI(file);
253            }
254
255            URLStreamHandler handler = new VFSURLStreamHandler(path);
256            return new URL(null, path.getURI(), handler);
257        }
258        catch (FileSystemException fse)
259        {
260            throw new ConfigurationRuntimeException("Could not parse basePath: " + basePath
261                + " and fileName: " + file, fse);
262        }
263    }
264
265    @Override
266    public URL locateFromURL(String basePath, String fileName)
267    {
268        String fileScheme = UriParser.extractScheme(fileName);
269
270        // Use DefaultFileSystem if basePath and fileName don't have a scheme.
271        if ((basePath == null || UriParser.extractScheme(basePath) == null) && fileScheme == null)
272        {
273            return super.locateFromURL(basePath, fileName);
274        }
275        try
276        {
277            FileSystemManager fsManager = VFS.getManager();
278
279            FileObject file;
280            // Only use the base path if the file name doesn't have a scheme.
281            if (basePath != null && fileScheme == null)
282            {
283                String scheme = UriParser.extractScheme(basePath);
284                FileSystemOptions opts = (scheme != null) ? getOptions(scheme) : null;
285                FileObject base = (opts == null) ? fsManager.resolveFile(basePath)
286                        : fsManager.resolveFile(basePath, opts);
287                if (base.getType() == FileType.FILE)
288                {
289                    base = base.getParent();
290                }
291
292                file = fsManager.resolveFile(base, fileName);
293            }
294            else
295            {
296                FileSystemOptions opts = (fileScheme != null) ? getOptions(fileScheme) : null;
297                file = (opts == null) ? fsManager.resolveFile(fileName)
298                        : fsManager.resolveFile(fileName, opts);
299            }
300
301            if (!file.exists())
302            {
303                return null;
304            }
305            FileName path = file.getName();
306            URLStreamHandler handler = new VFSURLStreamHandler(path);
307            return new URL(null, path.getURI(), handler);
308        }
309        catch (FileSystemException fse)
310        {
311            return null;
312        }
313        catch (MalformedURLException ex)
314        {
315            return null;
316        }
317    }
318
319    private FileSystemOptions getOptions(String scheme)
320    {
321        FileSystemOptions opts = new FileSystemOptions();
322        FileSystemConfigBuilder builder;
323        try
324        {
325            builder = VFS.getManager().getFileSystemConfigBuilder(scheme);
326        }
327        catch (Exception ex)
328        {
329            return null;
330        }
331        FileOptionsProvider provider = getFileOptionsProvider();
332        if (provider != null)
333        {
334            Map<String, Object> map = provider.getOptions();
335            if (map == null)
336            {
337                return null;
338            }
339            int count = 0;
340            for (Map.Entry<String, Object> entry : map.entrySet())
341            {
342                try
343                {
344                    String key = entry.getKey();
345                    if (FileOptionsProvider.CURRENT_USER.equals(key))
346                    {
347                        key = "creatorName";
348                    }
349                    setProperty(builder, opts, key, entry.getValue());
350                    ++count;
351                }
352                catch (Exception ex)
353                {
354                    // Ignore an incorrect property.
355                    continue;
356                }
357            }
358            if (count > 0)
359            {
360                return opts;
361            }
362        }
363        return null;
364
365    }
366
367    private void setProperty(FileSystemConfigBuilder builder, FileSystemOptions options,
368                             String key, Object value)
369    {
370        String methodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
371        Class<?>[] paramTypes = new Class<?>[2];
372        paramTypes[0] = FileSystemOptions.class;
373        paramTypes[1] = value.getClass();
374
375        try
376        {
377            Method method = builder.getClass().getMethod(methodName, paramTypes);
378            Object[] params = new Object[2];
379            params[0] = options;
380            params[1] = value;
381            method.invoke(builder, params);
382        }
383        catch (Exception ex)
384        {
385            return;
386        }
387
388    }
389
390    /**
391     * Stream handler required to create URL.
392     */
393    private static class VFSURLStreamHandler extends URLStreamHandler
394    {
395        /** The Protocol used */
396        private final String protocol;
397
398        public VFSURLStreamHandler(FileName file)
399        {
400            this.protocol = file.getScheme();
401        }
402
403        @Override
404        protected URLConnection openConnection(URL url) throws IOException
405        {
406            throw new IOException("VFS URLs can only be used with VFS APIs");
407        }
408    }
409}