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 java.io.File;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.OutputStream;
023    import java.lang.reflect.Method;
024    import java.net.MalformedURLException;
025    import java.net.URL;
026    import java.net.URLConnection;
027    import java.net.URLStreamHandler;
028    import java.util.Map;
029    
030    import org.apache.commons.vfs2.FileContent;
031    import org.apache.commons.vfs2.FileName;
032    import org.apache.commons.vfs2.FileObject;
033    import org.apache.commons.vfs2.FileSystemConfigBuilder;
034    import org.apache.commons.vfs2.FileSystemException;
035    import org.apache.commons.vfs2.FileSystemManager;
036    import org.apache.commons.vfs2.FileSystemOptions;
037    import org.apache.commons.vfs2.FileType;
038    import org.apache.commons.vfs2.VFS;
039    import 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.java 1210205 2011-12-04 20:38:19Z oheger $
047     */
048    public 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    }