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    *     https://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       * Constructs a new instance.
96       */
97      public DefaultFileSystem() {
98          // empty
99      }
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 }