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.VFS; 042import org.apache.commons.vfs2.provider.UriParser; 043 044/** 045 * FileSystem that uses <a href="https://commons.apache.org/proper/commons-vfs/">Apache Commons VFS</a>. 046 * 047 * @since 1.7 048 */ 049public class VFSFileSystem extends DefaultFileSystem { 050 /** 051 * Stream handler required to create URL. 052 */ 053 private static class VFSURLStreamHandler extends URLStreamHandler { 054 /** The Protocol used */ 055 private final String protocol; 056 057 public VFSURLStreamHandler(final FileName file) { 058 this.protocol = file.getScheme(); 059 } 060 061 @Override 062 protected URLConnection openConnection(final URL url) throws IOException { 063 throw new IOException("VFS URLs can only be used with VFS APIs"); 064 } 065 } 066 067 /** The logger. */ 068 private final Log log = LogFactory.getLog(getClass()); 069 070 public VFSFileSystem() { 071 // empty 072 } 073 074 @Override 075 public String getBasePath(final String path) { 076 if (UriParser.extractScheme(path) == null) { 077 return super.getBasePath(path); 078 } 079 try { 080 final FileName parent = resolveURI(path).getParent(); 081 return parent != null ? parent.getURI() : null; 082 } catch (final FileSystemException fse) { 083 fse.printStackTrace(); 084 return null; 085 } 086 } 087 088 @Override 089 public String getFileName(final String path) { 090 if (UriParser.extractScheme(path) == null) { 091 return super.getFileName(path); 092 } 093 try { 094 return resolveURI(path).getBaseName(); 095 } catch (final FileSystemException fse) { 096 fse.printStackTrace(); 097 return null; 098 } 099 } 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}