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