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