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