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      @Override
40      public InputStream getInputStream(final URL url) throws ConfigurationException {
41          return getInputStream(url, null);
42      }
43  
44      @Override
45      public InputStream getInputStream(final URL url, final URLConnectionOptions urlConnectionOptions) throws ConfigurationException {
46          // throw an exception if the target URL is a directory
47          final File file = FileLocatorUtils.fileFromURL(url);
48          if (file != null && file.isDirectory()) {
49              throw new ConfigurationException("Cannot load a configuration from a directory");
50          }
51  
52          try {
53              return urlConnectionOptions == null ? url.openStream() : urlConnectionOptions.openConnection(url).getInputStream();
54          } catch (final Exception e) {
55              throw new ConfigurationException("Unable to load the configuration from the URL " + url, e);
56          }
57      }
58  
59      @Override
60      public OutputStream getOutputStream(final URL url) throws ConfigurationException {
61          // file URLs have to be converted to Files since FileURLConnection is
62          // read only (https://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4191800)
63          final File file = FileLocatorUtils.fileFromURL(url);
64          if (file != null) {
65              return getOutputStream(file);
66          }
67          // for non file URLs save through an URLConnection
68          OutputStream out;
69          try {
70              final URLConnection connection = url.openConnection();
71              connection.setDoOutput(true);
72  
73              // use the PUT method for http URLs
74              if (connection instanceof HttpURLConnection) {
75                  final HttpURLConnection conn = (HttpURLConnection) connection;
76                  conn.setRequestMethod("PUT");
77              }
78  
79              out = connection.getOutputStream();
80  
81              // check the response code for http URLs and throw an exception if an error occurred
82              if (connection instanceof HttpURLConnection) {
83                  out = new HttpOutputStream(out, (HttpURLConnection) connection);
84              }
85              return out;
86          } catch (final IOException e) {
87              throw new ConfigurationException("Could not save to URL " + url, e);
88          }
89      }
90  
91      @Override
92      public OutputStream getOutputStream(final File file) throws ConfigurationException {
93          try {
94              // create the file if necessary
95              createPath(file);
96              return new FileOutputStream(file);
97          } catch (final FileNotFoundException e) {
98              throw new ConfigurationException("Unable to save to file " + file, e);
99          }
100     }
101 
102     @Override
103     public String getPath(final File file, final URL url, final String basePath, final String fileName) {
104         String path = null;
105         // if resource was loaded from jar file may be null
106         if (file != null) {
107             path = file.getAbsolutePath();
108         }
109 
110         // try to see if file was loaded from a jar
111         if (path == null) {
112             if (url != null) {
113                 path = url.getPath();
114             } else {
115                 try {
116                     path = getURL(basePath, fileName).getPath();
117                 } catch (final Exception e) {
118                     // simply ignore it and return null
119                     if (getLogger().isDebugEnabled()) {
120                         getLogger().debug(String.format("Could not determine URL for " + "basePath = %s, fileName = %s: %s", basePath, fileName, e));
121                     }
122                 }
123             }
124         }
125 
126         return path;
127     }
128 
129     @Override
130     public String getBasePath(final String path) {
131         final URL url;
132         try {
133             url = getURL(null, path);
134             return FileLocatorUtils.getBasePath(url);
135         } catch (final Exception e) {
136             return null;
137         }
138     }
139 
140     @Override
141     public String getFileName(final String path) {
142         final URL url;
143         try {
144             url = getURL(null, path);
145             return FileLocatorUtils.getFileName(url);
146         } catch (final Exception e) {
147             return null;
148         }
149     }
150 
151     @Override
152     public URL getURL(final String basePath, final String file) throws MalformedURLException {
153         final File f = new File(file);
154         // already absolute?
155         if (f.isAbsolute()) {
156             return FileLocatorUtils.toURL(f);
157         }
158 
159         try {
160             if (basePath == null) {
161                 return new URL(file);
162             }
163             final URL base = new URL(basePath);
164             return new URL(base, file);
165         } catch (final MalformedURLException uex) {
166             return FileLocatorUtils.toURL(FileLocatorUtils.constructFile(basePath, file));
167         }
168     }
169 
170     @Override
171     public URL locateFromURL(final String basePath, final String fileName) {
172         try {
173             final URL url;
174             if (basePath == null) {
175                 return new URL(fileName);
176                 // url = new URL(name);
177             }
178             final URL baseURL = new URL(basePath);
179             url = new URL(baseURL, fileName);
180 
181             // check if the file exists
182             try (InputStream in = url.openStream()) {
183                 // nothing
184                 in.available();
185             }
186             return url;
187         } catch (final IOException e) {
188             if (getLogger().isDebugEnabled()) {
189                 getLogger().debug("Could not locate file " + fileName + " at " + basePath + ": " + e.getMessage());
190             }
191             return null;
192         }
193     }
194 
195     /**
196      * Create the path to the specified file.
197      *
198      * @param file the target file
199      * @throws ConfigurationException if the path cannot be created
200      */
201     private void createPath(final File file) throws ConfigurationException {
202         // create the path to the file if the file doesn't exist
203         if (file != null && !file.exists()) {
204             final File parent = file.getParentFile();
205             if (parent != null && !parent.exists() && !parent.mkdirs()) {
206                 throw new ConfigurationException("Cannot create path: " + parent);
207             }
208         }
209     }
210 
211     /**
212      * Wraps the output stream so errors can be detected in the HTTP response.
213      *
214      * @since 1.7
215      */
216     private static final class HttpOutputStream extends VerifiableOutputStream {
217         /** The wrapped OutputStream */
218         private final OutputStream stream;
219 
220         /** The HttpURLConnection */
221         private final HttpURLConnection connection;
222 
223         public HttpOutputStream(final OutputStream stream, final HttpURLConnection connection) {
224             this.stream = stream;
225             this.connection = connection;
226         }
227 
228         @Override
229         public void write(final byte[] bytes) throws IOException {
230             stream.write(bytes);
231         }
232 
233         @Override
234         public void write(final byte[] bytes, final int i, final int i1) throws IOException {
235             stream.write(bytes, i, i1);
236         }
237 
238         @Override
239         public void flush() throws IOException {
240             stream.flush();
241         }
242 
243         @Override
244         public void close() throws IOException {
245             stream.close();
246         }
247 
248         @Override
249         public void write(final int i) throws IOException {
250             stream.write(i);
251         }
252 
253         @Override
254         public String toString() {
255             return stream.toString();
256         }
257 
258         @Override
259         public void verify() throws IOException {
260             if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
261                 throw new IOException("HTTP Error " + connection.getResponseCode() + " " + connection.getResponseMessage());
262             }
263         }
264     }
265 }