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 * https://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.FileNotFoundException; 021import java.io.FileOutputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.OutputStream; 025import java.net.HttpURLConnection; 026import java.net.MalformedURLException; 027import java.net.URL; 028import java.net.URLConnection; 029 030import org.apache.commons.configuration2.ex.ConfigurationException; 031 032/** 033 * FileSystem that uses java.io.File or HttpClient. 034 * 035 * @since 1.7 036 */ 037public class DefaultFileSystem extends FileSystem { 038 039 /** 040 * Wraps the output stream so errors can be detected in the HTTP response. 041 * 042 * @since 1.7 043 */ 044 private static final class HttpOutputStream extends VerifiableOutputStream { 045 /** The wrapped OutputStream */ 046 private final OutputStream stream; 047 048 /** The HttpURLConnection */ 049 private final HttpURLConnection connection; 050 051 public HttpOutputStream(final OutputStream stream, final HttpURLConnection connection) { 052 this.stream = stream; 053 this.connection = connection; 054 } 055 056 @Override 057 public void close() throws IOException { 058 stream.close(); 059 } 060 061 @Override 062 public void flush() throws IOException { 063 stream.flush(); 064 } 065 066 @Override 067 public String toString() { 068 return stream.toString(); 069 } 070 071 @Override 072 public void verify() throws IOException { 073 if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) { 074 throw new IOException("HTTP Error " + connection.getResponseCode() + " " + connection.getResponseMessage()); 075 } 076 } 077 078 @Override 079 public void write(final byte[] bytes) throws IOException { 080 stream.write(bytes); 081 } 082 083 @Override 084 public void write(final byte[] bytes, final int i, final int i1) throws IOException { 085 stream.write(bytes, i, i1); 086 } 087 088 @Override 089 public void write(final int i) throws IOException { 090 stream.write(i); 091 } 092 } 093 094 /** 095 * Constructs a new instance. 096 */ 097 public DefaultFileSystem() { 098 // empty 099 } 100 101 /** 102 * Create the path to the specified file. 103 * 104 * @param file the target file 105 * @throws ConfigurationException if the path cannot be created 106 */ 107 private void createPath(final File file) throws ConfigurationException { 108 // create the path to the file if the file doesn't exist 109 if (file != null && !file.exists()) { 110 final File parent = file.getParentFile(); 111 if (parent != null && !parent.exists() && !parent.mkdirs()) { 112 throw new ConfigurationException("Cannot create path: " + parent); 113 } 114 } 115 } 116 117 @Override 118 public String getBasePath(final String path) { 119 final URL url; 120 try { 121 url = getURL(null, path); 122 return FileLocatorUtils.getBasePath(url); 123 } catch (final Exception e) { 124 return null; 125 } 126 } 127 128 @Override 129 public String getFileName(final String path) { 130 final URL url; 131 try { 132 url = getURL(null, path); 133 return FileLocatorUtils.getFileName(url); 134 } catch (final Exception e) { 135 return null; 136 } 137 } 138 139 @Override 140 public InputStream getInputStream(final URL url) throws ConfigurationException { 141 return getInputStream(url, null); 142 } 143 144 @Override 145 public InputStream getInputStream(final URL url, final URLConnectionOptions urlConnectionOptions) throws ConfigurationException { 146 // throw an exception if the target URL is a directory 147 final File file = FileLocatorUtils.fileFromURL(url); 148 if (file != null && file.isDirectory()) { 149 throw new ConfigurationException("Cannot load a configuration from a directory"); 150 } 151 152 try { 153 return urlConnectionOptions == null ? url.openStream() : urlConnectionOptions.openConnection(url).getInputStream(); 154 } catch (final Exception e) { 155 throw new ConfigurationException("Unable to load the configuration from the URL " + url, e); 156 } 157 } 158 159 @Override 160 public OutputStream getOutputStream(final File file) throws ConfigurationException { 161 try { 162 // create the file if necessary 163 createPath(file); 164 return new FileOutputStream(file); 165 } catch (final FileNotFoundException e) { 166 throw new ConfigurationException("Unable to save to file " + file, e); 167 } 168 } 169 170 @Override 171 public OutputStream getOutputStream(final URL url) throws ConfigurationException { 172 // file URLs have to be converted to Files since FileURLConnection is 173 // read only (https://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4191800) 174 final File file = FileLocatorUtils.fileFromURL(url); 175 if (file != null) { 176 return getOutputStream(file); 177 } 178 // for non file URLs save through an URLConnection 179 OutputStream out; 180 try { 181 final URLConnection connection = url.openConnection(); 182 connection.setDoOutput(true); 183 184 // use the PUT method for http URLs 185 if (connection instanceof HttpURLConnection) { 186 final HttpURLConnection conn = (HttpURLConnection) connection; 187 conn.setRequestMethod("PUT"); 188 } 189 190 out = connection.getOutputStream(); 191 192 // check the response code for http URLs and throw an exception if an error occurred 193 if (connection instanceof HttpURLConnection) { 194 out = new HttpOutputStream(out, (HttpURLConnection) connection); 195 } 196 return out; 197 } catch (final IOException e) { 198 throw new ConfigurationException("Could not save to URL " + url, e); 199 } 200 } 201 202 @Override 203 public String getPath(final File file, final URL url, final String basePath, final String fileName) { 204 String path = null; 205 // if resource was loaded from jar file may be null 206 if (file != null) { 207 path = file.getAbsolutePath(); 208 } 209 210 // try to see if file was loaded from a jar 211 if (path == null) { 212 if (url != null) { 213 path = url.getPath(); 214 } else { 215 try { 216 path = getURL(basePath, fileName).getPath(); 217 } catch (final Exception e) { 218 // simply ignore it and return null 219 if (getLogger().isDebugEnabled()) { 220 getLogger().debug(String.format("Could not determine URL for basePath = %s, fileName = %s: %s", basePath, fileName, e)); 221 } 222 } 223 } 224 } 225 226 return path; 227 } 228 229 @Override 230 public URL getURL(final String basePath, final String file) throws MalformedURLException { 231 final File f = new File(file); 232 // already absolute? 233 if (f.isAbsolute()) { 234 return FileLocatorUtils.toURL(f); 235 } 236 237 try { 238 if (basePath == null) { 239 return new URL(file); 240 } 241 final URL base = new URL(basePath); 242 return new URL(base, file); 243 } catch (final MalformedURLException uex) { 244 return FileLocatorUtils.toURL(FileLocatorUtils.constructFile(basePath, file)); 245 } 246 } 247 248 @Override 249 public URL locateFromURL(final String basePath, final String fileName) { 250 try { 251 final URL url; 252 if (basePath == null) { 253 return new URL(fileName); 254 // url = new URL(name); 255 } 256 final URL baseURL = new URL(basePath); 257 url = new URL(baseURL, fileName); 258 259 // check if the file exists 260 try (InputStream in = url.openStream()) { 261 // nothing 262 in.available(); 263 } 264 return url; 265 } catch (final IOException e) { 266 if (getLogger().isDebugEnabled()) { 267 getLogger().debug("Could not locate file " + fileName + " at " + basePath + ": " + e.getMessage()); 268 } 269 return null; 270 } 271 } 272}