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 /** 052 * Stream handler required to create URL. 053 */ 054 private static final class VFSURLStreamHandler extends URLStreamHandler { 055 056 @Override 057 protected URLConnection openConnection(final URL url) throws IOException { 058 throw new IOException("VFS URLs can only be used with VFS APIs"); 059 } 060 } 061 062 /** The logger. */ 063 private final Log log = LogFactory.getLog(getClass()); 064 065 public VFSFileSystem() { 066 // empty 067 } 068 069 @Override 070 public String getBasePath(final String path) { 071 if (UriParser.extractScheme(path) == null) { 072 return super.getBasePath(path); 073 } 074 try { 075 final FileName parent = resolveURI(path).getParent(); 076 return parent != null ? parent.getURI() : null; 077 } catch (final FileSystemException fse) { 078 fse.printStackTrace(); 079 return null; 080 } 081 } 082 083 @Override 084 public String getFileName(final String path) { 085 if (UriParser.extractScheme(path) == null) { 086 return super.getFileName(path); 087 } 088 try { 089 return resolveURI(path).getBaseName(); 090 } catch (final FileSystemException fse) { 091 fse.printStackTrace(); 092 return null; 093 } 094 } 095 096 @Override 097 public InputStream getInputStream(final URL url) throws ConfigurationException { 098 final FileObject file; 099 try { 100 final FileSystemOptions opts = getOptions(url.getProtocol()); 101 file = getManager().resolveFile(url.toString(), opts); 102 if (!file.exists()) { 103 throw new ConfigurationException("File not found"); 104 } 105 if (!file.isFile()) { 106 throw new ConfigurationException("Cannot load a configuration from a directory"); 107 } 108 final FileContent content = file.getContent(); 109 if (content == null) { 110 final String msg = "Cannot access content of " + file.getName().getFriendlyURI(); 111 throw new ConfigurationException(msg); 112 } 113 return content.getInputStream(); 114 } catch (final FileSystemException fse) { 115 final String msg = "Unable to access " + url.toString(); 116 throw new ConfigurationException(msg, fse); 117 } 118 } 119 120 private FileSystemManager getManager() throws FileSystemException { 121 return VFS.getManager(); 122 } 123 124 private FileSystemOptions getOptions(final String scheme) { 125 if (scheme == null) { 126 return null; 127 } 128 final FileSystemOptions opts = new FileSystemOptions(); 129 final FileSystemConfigBuilder builder; 130 try { 131 builder = getManager().getFileSystemConfigBuilder(scheme); 132 } catch (final Exception ex) { 133 return null; 134 } 135 final FileOptionsProvider provider = getFileOptionsProvider(); 136 if (provider != null) { 137 final Map<String, Object> map = provider.getOptions(); 138 if (map == null) { 139 return null; 140 } 141 int count = 0; 142 for (final Map.Entry<String, Object> entry : map.entrySet()) { 143 try { 144 String key = entry.getKey(); 145 if (FileOptionsProvider.CURRENT_USER.equals(key)) { 146 key = "creatorName"; 147 } 148 setProperty(builder, opts, key, entry.getValue()); 149 ++count; 150 } catch (final Exception ex) { 151 // Ignore an incorrect property. 152 continue; 153 } 154 } 155 if (count > 0) { 156 return opts; 157 } 158 } 159 return null; 160 161 } 162 163 @Override 164 public OutputStream getOutputStream(final URL url) throws ConfigurationException { 165 try { 166 final FileSystemOptions opts = getOptions(url.getProtocol()); 167 final FileObject file = getManager().resolveFile(url.toString(), opts); 168 // throw an exception if the target URL is a directory 169 if (file == null || file.isFolder()) { 170 throw new ConfigurationException("Cannot save a configuration to a directory"); 171 } 172 final FileContent content = file.getContent(); 173 174 if (content == null) { 175 throw new ConfigurationException("Cannot access content of " + url); 176 } 177 return content.getOutputStream(); 178 } catch (final FileSystemException fse) { 179 throw new ConfigurationException("Unable to access " + url, fse); 180 } 181 } 182 183 @Override 184 public String getPath(final File file, final URL url, final String basePath, final String fileName) { 185 if (file != null) { 186 return super.getPath(file, url, basePath, fileName); 187 } 188 try { 189 if (url != null) { 190 final FileName name = resolveURI(url.toString()); 191 if (name != null) { 192 return name.toString(); 193 } 194 } 195 if (UriParser.extractScheme(fileName) != null) { 196 return fileName; 197 } 198 if (basePath != null) { 199 final FileName base = resolveURI(basePath); 200 return getManager().resolveName(base, fileName).getURI(); 201 } 202 final FileName name = resolveURI(fileName); 203 final FileName base = name.getParent(); 204 return getManager().resolveName(base, name.getBaseName()).getURI(); 205 } catch (final FileSystemException fse) { 206 fse.printStackTrace(); 207 return null; 208 } 209 } 210 211 @Override 212 public URL getURL(final String basePath, final String file) throws MalformedURLException { 213 if (basePath != null && UriParser.extractScheme(basePath) == null || basePath == null && UriParser.extractScheme(file) == null) { 214 return super.getURL(basePath, file); 215 } 216 try { 217 final FileName path; 218 if (basePath != null && UriParser.extractScheme(file) == null) { 219 final FileName base = resolveURI(basePath); 220 path = getManager().resolveName(base, file); 221 } else { 222 path = resolveURI(file); 223 } 224 225 final URLStreamHandler handler = new VFSURLStreamHandler(); 226 return new URL(null, path.getURI(), handler); 227 } catch (final FileSystemException fse) { 228 throw new ConfigurationRuntimeException("Could not parse basePath: " + basePath + " and fileName: " + file, fse); 229 } 230 } 231 232 @Override 233 public URL locateFromURL(final String basePath, final String fileName) { 234 final String fileScheme = UriParser.extractScheme(fileName); 235 // Use DefaultFileSystem if basePath and fileName don't have a scheme. 236 if ((basePath == null || UriParser.extractScheme(basePath) == null) && fileScheme == null) { 237 return super.locateFromURL(basePath, fileName); 238 } 239 try { 240 final FileObject file; 241 // Only use the base path if the file name doesn't have a scheme. 242 if (basePath != null && fileScheme == null) { 243 final String scheme = UriParser.extractScheme(basePath); 244 final FileSystemOptions opts = getOptions(scheme); 245 FileObject base = getManager().resolveFile(basePath, opts); 246 if (base.isFile()) { 247 base = base.getParent(); 248 } 249 250 file = getManager().resolveFile(base, fileName); 251 } else { 252 final FileSystemOptions opts = getOptions(fileScheme); 253 file = getManager().resolveFile(fileName, opts); 254 } 255 256 if (!file.exists()) { 257 return null; 258 } 259 final FileName path = file.getName(); 260 final URLStreamHandler handler = new VFSURLStreamHandler(); 261 return new URL(null, path.getURI(), handler); 262 } catch (final FileSystemException | MalformedURLException fse) { 263 return null; 264 } 265 } 266 267 private FileName resolveURI(final String path) throws FileSystemException { 268 return getManager().resolveURI(path); 269 } 270 271 private void setProperty(final FileSystemConfigBuilder builder, final FileSystemOptions options, final String key, final Object value) { 272 final String methodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1); 273 final Class<?>[] paramTypes = new Class<?>[2]; 274 paramTypes[0] = FileSystemOptions.class; 275 paramTypes[1] = value.getClass(); 276 try { 277 final Method method = builder.getClass().getMethod(methodName, paramTypes); 278 final Object[] params = new Object[2]; 279 params[0] = options; 280 params[1] = value; 281 method.invoke(builder, params); 282 } catch (final Exception ex) { 283 log.warn("Cannot access property '" + key + "'! Ignoring.", ex); 284 } 285 } 286}