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  
46          /** The wrapped OutputStream */
47          private final OutputStream stream;
48  
49          /** The HttpURLConnection */
50          private final HttpURLConnection connection;
51  
52          public HttpOutputStream(final OutputStream stream, final HttpURLConnection connection) {
53              this.stream = stream;
54              this.connection = connection;
55          }
56  
57          @Override
58          public void close() throws IOException {
59              stream.close();
60          }
61  
62          @Override
63          public void flush() throws IOException {
64              stream.flush();
65          }
66  
67          @Override
68          public String toString() {
69              return stream.toString();
70          }
71  
72          @Override
73          public void verify() throws IOException {
74              if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
75                  throw new IOException("HTTP Error " + connection.getResponseCode() + " " + connection.getResponseMessage());
76              }
77          }
78  
79          @Override
80          public void write(final byte[] bytes) throws IOException {
81              stream.write(bytes);
82          }
83  
84          @Override
85          public void write(final byte[] bytes, final int i, final int i1) throws IOException {
86              stream.write(bytes, i, i1);
87          }
88  
89          @Override
90          public void write(final int i) throws IOException {
91              stream.write(i);
92          }
93      }
94  
95      /**
96       * Constructs a new instance.
97       */
98      public DefaultFileSystem() {
99          // 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 }