View Javadoc
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  
19  import java.io.File;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.lang.reflect.Method;
24  import java.net.MalformedURLException;
25  import java.net.URL;
26  import java.net.URLConnection;
27  import java.net.URLStreamHandler;
28  import java.util.Map;
29  
30  import org.apache.commons.configuration2.ex.ConfigurationException;
31  import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.commons.vfs2.FileContent;
35  import org.apache.commons.vfs2.FileName;
36  import org.apache.commons.vfs2.FileObject;
37  import org.apache.commons.vfs2.FileSystemConfigBuilder;
38  import org.apache.commons.vfs2.FileSystemException;
39  import org.apache.commons.vfs2.FileSystemManager;
40  import org.apache.commons.vfs2.FileSystemOptions;
41  import org.apache.commons.vfs2.VFS;
42  import org.apache.commons.vfs2.provider.UriParser;
43  
44  /**
45   * FileSystem that uses <a href="https://commons.apache.org/proper/commons-vfs/">Apache Commons VFS</a>.
46   *
47   * @since 1.7
48   */
49  public class VFSFileSystem extends DefaultFileSystem {
50      /**
51       * Stream handler required to create URL.
52       */
53      private static final class VFSURLStreamHandler extends URLStreamHandler {
54          /** The Protocol used */
55          private final String protocol;
56  
57          public VFSURLStreamHandler(final FileName file) {
58              this.protocol = file.getScheme();
59          }
60  
61          @Override
62          protected URLConnection openConnection(final URL url) throws IOException {
63              throw new IOException("VFS URLs can only be used with VFS APIs");
64          }
65      }
66  
67      /** The logger. */
68      private final Log log = LogFactory.getLog(getClass());
69  
70      public VFSFileSystem() {
71          // empty
72      }
73  
74      @Override
75      public String getBasePath(final String path) {
76          if (UriParser.extractScheme(path) == null) {
77              return super.getBasePath(path);
78          }
79          try {
80              final FileName parent = resolveURI(path).getParent();
81              return parent != null ? parent.getURI() : null;
82          } catch (final FileSystemException fse) {
83              fse.printStackTrace();
84              return null;
85          }
86      }
87  
88      @Override
89      public String getFileName(final String path) {
90          if (UriParser.extractScheme(path) == null) {
91              return super.getFileName(path);
92          }
93          try {
94              return resolveURI(path).getBaseName();
95          } catch (final FileSystemException fse) {
96              fse.printStackTrace();
97              return null;
98          }
99      }
100 
101     @Override
102     public InputStream getInputStream(final URL url) throws ConfigurationException {
103         final FileObject file;
104         try {
105             final FileSystemOptions opts = getOptions(url.getProtocol());
106             file = getManager().resolveFile(url.toString(), opts);
107             if (!file.exists()) {
108                 throw new ConfigurationException("File not found");
109             }
110             if (!file.isFile()) {
111                 throw new ConfigurationException("Cannot load a configuration from a directory");
112             }
113             final FileContent content = file.getContent();
114             if (content == null) {
115                 final String msg = "Cannot access content of " + file.getName().getFriendlyURI();
116                 throw new ConfigurationException(msg);
117             }
118             return content.getInputStream();
119         } catch (final FileSystemException fse) {
120             final String msg = "Unable to access " + url.toString();
121             throw new ConfigurationException(msg, fse);
122         }
123     }
124 
125     private FileSystemManager getManager() throws FileSystemException {
126         return VFS.getManager();
127     }
128 
129     private FileSystemOptions getOptions(final String scheme) {
130         if (scheme == null) {
131             return null;
132         }
133         final FileSystemOptions opts = new FileSystemOptions();
134         final FileSystemConfigBuilder builder;
135         try {
136             builder = getManager().getFileSystemConfigBuilder(scheme);
137         } catch (final Exception ex) {
138             return null;
139         }
140         final FileOptionsProvider provider = getFileOptionsProvider();
141         if (provider != null) {
142             final Map<String, Object> map = provider.getOptions();
143             if (map == null) {
144                 return null;
145             }
146             int count = 0;
147             for (final Map.Entry<String, Object> entry : map.entrySet()) {
148                 try {
149                     String key = entry.getKey();
150                     if (FileOptionsProvider.CURRENT_USER.equals(key)) {
151                         key = "creatorName";
152                     }
153                     setProperty(builder, opts, key, entry.getValue());
154                     ++count;
155                 } catch (final Exception ex) {
156                     // Ignore an incorrect property.
157                     continue;
158                 }
159             }
160             if (count > 0) {
161                 return opts;
162             }
163         }
164         return null;
165 
166     }
167 
168     @Override
169     public OutputStream getOutputStream(final URL url) throws ConfigurationException {
170         try {
171             final FileSystemOptions opts = getOptions(url.getProtocol());
172             final FileObject file = getManager().resolveFile(url.toString(), opts);
173             // throw an exception if the target URL is a directory
174             if (file == null || file.isFolder()) {
175                 throw new ConfigurationException("Cannot save a configuration to a directory");
176             }
177             final FileContent content = file.getContent();
178 
179             if (content == null) {
180                 throw new ConfigurationException("Cannot access content of " + url);
181             }
182             return content.getOutputStream();
183         } catch (final FileSystemException fse) {
184             throw new ConfigurationException("Unable to access " + url, fse);
185         }
186     }
187 
188     @Override
189     public String getPath(final File file, final URL url, final String basePath, final String fileName) {
190         if (file != null) {
191             return super.getPath(file, url, basePath, fileName);
192         }
193         try {
194             if (url != null) {
195                 final FileName name = resolveURI(url.toString());
196                 if (name != null) {
197                     return name.toString();
198                 }
199             }
200 
201             if (UriParser.extractScheme(fileName) != null) {
202                 return fileName;
203             }
204             if (basePath != null) {
205                 final FileName base = resolveURI(basePath);
206                 return getManager().resolveName(base, fileName).getURI();
207             }
208             final FileName name = resolveURI(fileName);
209             final FileName base = name.getParent();
210             return getManager().resolveName(base, name.getBaseName()).getURI();
211         } catch (final FileSystemException fse) {
212             fse.printStackTrace();
213             return null;
214         }
215     }
216 
217     @Override
218     public URL getURL(final String basePath, final String file) throws MalformedURLException {
219         if (basePath != null && UriParser.extractScheme(basePath) == null || basePath == null && UriParser.extractScheme(file) == null) {
220             return super.getURL(basePath, file);
221         }
222         try {
223             final FileName path;
224             if (basePath != null && UriParser.extractScheme(file) == null) {
225                 final FileName base = resolveURI(basePath);
226                 path = getManager().resolveName(base, file);
227             } else {
228                 path = resolveURI(file);
229             }
230 
231             final URLStreamHandler handler = new VFSURLStreamHandler(path);
232             return new URL(null, path.getURI(), handler);
233         } catch (final FileSystemException fse) {
234             throw new ConfigurationRuntimeException("Could not parse basePath: " + basePath + " and fileName: " + file, fse);
235         }
236     }
237 
238     @Override
239     public URL locateFromURL(final String basePath, final String fileName) {
240         final String fileScheme = UriParser.extractScheme(fileName);
241 
242         // Use DefaultFileSystem if basePath and fileName don't have a scheme.
243         if ((basePath == null || UriParser.extractScheme(basePath) == null) && fileScheme == null) {
244             return super.locateFromURL(basePath, fileName);
245         }
246         try {
247             final FileObject file;
248             // Only use the base path if the file name doesn't have a scheme.
249             if (basePath != null && fileScheme == null) {
250                 final String scheme = UriParser.extractScheme(basePath);
251                 final FileSystemOptions opts = getOptions(scheme);
252                 FileObject base = getManager().resolveFile(basePath, opts);
253                 if (base.isFile()) {
254                     base = base.getParent();
255                 }
256 
257                 file = getManager().resolveFile(base, fileName);
258             } else {
259                 final FileSystemOptions opts = getOptions(fileScheme);
260                 file = getManager().resolveFile(fileName, opts);
261             }
262 
263             if (!file.exists()) {
264                 return null;
265             }
266             final FileName path = file.getName();
267             final URLStreamHandler handler = new VFSURLStreamHandler(path);
268             return new URL(null, path.getURI(), handler);
269         } catch (final FileSystemException | MalformedURLException fse) {
270             return null;
271         }
272     }
273 
274     private FileName resolveURI(final String path) throws FileSystemException {
275         return getManager().resolveURI(path);
276     }
277 
278     private void setProperty(final FileSystemConfigBuilder builder, final FileSystemOptions options, final String key, final Object value) {
279         final String methodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
280         final Class<?>[] paramTypes = new Class<?>[2];
281         paramTypes[0] = FileSystemOptions.class;
282         paramTypes[1] = value.getClass();
283 
284         try {
285             final Method method = builder.getClass().getMethod(methodName, paramTypes);
286             final Object[] params = new Object[2];
287             params[0] = options;
288             params[1] = value;
289             method.invoke(builder, params);
290         } catch (final Exception ex) {
291             log.warn("Cannot access property '" + key + "'! Ignoring.", ex);
292         }
293 
294     }
295 }