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