View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2.io;
18  
19  import java.io.File;
20  import java.io.FileNotFoundException;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.net.HttpURLConnection;
26  import java.net.MalformedURLException;
27  import java.net.URL;
28  import java.net.URLConnection;
29  
30  import org.apache.commons.configuration2.ex.ConfigurationException;
31  
32  /**
33   * FileSystem that uses java.io.File or HttpClient.
34   *
35   * @since 1.7
36   */
37  public class DefaultFileSystem extends FileSystem {
38  
39      /**
40       * Wraps the output stream so errors can be detected in the HTTP response.
41       *
42       * @since 1.7
43       */
44      private static final class HttpOutputStream extends VerifiableOutputStream {
45          /** The wrapped OutputStream */
46          private final OutputStream stream;
47  
48          /** The HttpURLConnection */
49          private final HttpURLConnection connection;
50  
51          public HttpOutputStream(final OutputStream stream, final HttpURLConnection connection) {
52              this.stream = stream;
53              this.connection = connection;
54          }
55  
56          @Override
57          public void close() throws IOException {
58              stream.close();
59          }
60  
61          @Override
62          public void flush() throws IOException {
63              stream.flush();
64          }
65  
66          @Override
67          public String toString() {
68              return stream.toString();
69          }
70  
71          @Override
72          public void verify() throws IOException {
73              if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
74                  throw new IOException("HTTP Error " + connection.getResponseCode() + " " + connection.getResponseMessage());
75              }
76          }
77  
78          @Override
79          public void write(final byte[] bytes) throws IOException {
80              stream.write(bytes);
81          }
82  
83          @Override
84          public void write(final byte[] bytes, final int i, final int i1) throws IOException {
85              stream.write(bytes, i, i1);
86          }
87  
88          @Override
89          public void write(final int i) throws IOException {
90              stream.write(i);
91          }
92      }
93  
94      /**
95       * Create the path to the specified file.
96       *
97       * @param file the target file
98       * @throws ConfigurationException if the path cannot be created
99       */
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 }