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.configuration;
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.logging.Log;
031import org.apache.commons.logging.LogFactory;
032
033/**
034 * FileSystem that uses java.io.File or HttpClient
035 * @since 1.7
036 * @author <a
037 * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
038 */
039public class DefaultFileSystem extends FileSystem
040{
041    /**
042     * The Log for diagnostic messages.
043     */
044    private Log log = LogFactory.getLog(DefaultFileSystem.class);
045
046    @Override
047    public InputStream getInputStream(String basePath, String fileName)
048        throws ConfigurationException
049    {
050        try
051        {
052            URL url = ConfigurationUtils.locate(this, basePath, fileName);
053
054            if (url == null)
055            {
056                throw new ConfigurationException("Cannot locate configuration source " + fileName);
057            }
058            return getInputStream(url);
059        }
060        catch (ConfigurationException e)
061        {
062            throw e;
063        }
064        catch (Exception e)
065        {
066            throw new ConfigurationException("Unable to load the configuration file " + fileName, e);
067        }
068    }
069
070    @Override
071    public InputStream getInputStream(URL url) throws ConfigurationException
072    {
073        // throw an exception if the target URL is a directory
074        File file = ConfigurationUtils.fileFromURL(url);
075        if (file != null && file.isDirectory())
076        {
077            throw new ConfigurationException("Cannot load a configuration from a directory");
078        }
079
080        try
081        {
082            return url.openStream();
083        }
084        catch (Exception e)
085        {
086            throw new ConfigurationException("Unable to load the configuration from the URL " + url, e);
087        }
088    }
089
090    @Override
091    public OutputStream getOutputStream(URL url) throws ConfigurationException
092    {
093        // file URLs have to be converted to Files since FileURLConnection is
094        // read only (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4191800)
095        File file = ConfigurationUtils.fileFromURL(url);
096        if (file != null)
097        {
098            return getOutputStream(file);
099        }
100        else
101        {
102            // for non file URLs save through an URLConnection
103            OutputStream out;
104            try
105            {
106                URLConnection connection = url.openConnection();
107                connection.setDoOutput(true);
108
109                // use the PUT method for http URLs
110                if (connection instanceof HttpURLConnection)
111                {
112                    HttpURLConnection conn = (HttpURLConnection) connection;
113                    conn.setRequestMethod("PUT");
114                }
115
116                out = connection.getOutputStream();
117
118                // check the response code for http URLs and throw an exception if an error occured
119                if (connection instanceof HttpURLConnection)
120                {
121                    out = new HttpOutputStream(out, (HttpURLConnection) connection);
122                }
123                return out;
124            }
125            catch (IOException e)
126            {
127                throw new ConfigurationException("Could not save to URL " + url, e);
128            }
129        }
130    }
131
132    @Override
133    public OutputStream getOutputStream(File file) throws ConfigurationException
134    {
135        try
136        {
137            // create the file if necessary
138            createPath(file);
139            return new FileOutputStream(file);
140        }
141        catch (FileNotFoundException e)
142        {
143            throw new ConfigurationException("Unable to save to file " + file, e);
144        }
145    }
146
147    @Override
148    public String getPath(File file, URL url, String basePath, String fileName)
149    {
150        String path = null;
151        // if resource was loaded from jar file may be null
152        if (file != null)
153        {
154            path = file.getAbsolutePath();
155        }
156
157        // try to see if file was loaded from a jar
158        if (path == null)
159        {
160            if (url != null)
161            {
162                path = url.getPath();
163            }
164            else
165            {
166                try
167                {
168                    path = getURL(basePath, fileName).getPath();
169                }
170                catch (Exception e)
171                {
172                    // simply ignore it and return null
173                    if (log.isDebugEnabled())
174                    {
175                        log.debug(String.format("Could not determine URL for "
176                                + "basePath = %s, fileName = %s.", basePath,
177                                fileName), e);
178                    }
179                }
180            }
181        }
182
183        return path;
184    }
185
186    @Override
187    public String getBasePath(String path)
188    {
189        URL url;
190        try
191        {
192            url = getURL(null, path);
193            return ConfigurationUtils.getBasePath(url);
194        }
195        catch (Exception e)
196        {
197            return null;
198        }
199    }
200
201    @Override
202    public String getFileName(String path)
203    {
204        URL url;
205        try
206        {
207            url = getURL(null, path);
208            return ConfigurationUtils.getFileName(url);
209        }
210        catch (Exception e)
211        {
212            return null;
213        }
214    }
215
216
217    @Override
218    public URL getURL(String basePath, String file) throws MalformedURLException
219    {
220        File f = new File(file);
221        if (f.isAbsolute()) // already absolute?
222        {
223            return ConfigurationUtils.toURL(f);
224        }
225
226        try
227        {
228            if (basePath == null)
229            {
230                return new URL(file);
231            }
232            else
233            {
234                URL base = new URL(basePath);
235                return new URL(base, file);
236            }
237        }
238        catch (MalformedURLException uex)
239        {
240            return ConfigurationUtils.toURL(ConfigurationUtils.constructFile(basePath, file));
241        }
242    }
243
244
245    @Override
246    public URL locateFromURL(String basePath, String fileName)
247    {
248        try
249        {
250            URL url;
251            if (basePath == null)
252            {
253                return new URL(fileName);
254                //url = new URL(name);
255            }
256            else
257            {
258                URL baseURL = new URL(basePath);
259                url = new URL(baseURL, fileName);
260
261                // check if the file exists
262                InputStream in = null;
263                try
264                {
265                    in = url.openStream();
266                }
267                finally
268                {
269                    if (in != null)
270                    {
271                        in.close();
272                    }
273                }
274                return url;
275            }
276        }
277        catch (IOException e)
278        {
279            if (log.isDebugEnabled())
280            {
281                log.debug("Could not locate file " + fileName + " at " + basePath + ": " + e.getMessage());
282            }
283            return null;
284        }
285    }
286
287    /**
288     * Create the path to the specified file.
289     *
290     * @param file the target file
291     */
292    private void createPath(File file)
293    {
294        if (file != null)
295        {
296            // create the path to the file if the file doesn't exist
297            if (!file.exists())
298            {
299                File parent = file.getParentFile();
300                if (parent != null && !parent.exists())
301                {
302                    parent.mkdirs();
303                }
304            }
305        }
306    }
307    /**
308     * Wraps the output stream so errors can be detected in the HTTP response.
309     * @since 1.7
310     * @author <a
311     * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
312     */
313    private static class HttpOutputStream extends VerifiableOutputStream
314    {
315        /** The wrapped OutputStream */
316        private final OutputStream stream;
317
318        /** The HttpURLConnection */
319        private final HttpURLConnection connection;
320
321        public HttpOutputStream(OutputStream stream, HttpURLConnection connection)
322        {
323            this.stream = stream;
324            this.connection = connection;
325        }
326
327        @Override
328        public void write(byte[] bytes) throws IOException
329        {
330            stream.write(bytes);
331        }
332
333        @Override
334        public void write(byte[] bytes, int i, int i1) throws IOException
335        {
336            stream.write(bytes, i, i1);
337        }
338
339        @Override
340        public void flush() throws IOException
341        {
342            stream.flush();
343        }
344
345        @Override
346        public void close() throws IOException
347        {
348            stream.close();
349        }
350
351        @Override
352        public void write(int i) throws IOException
353        {
354            stream.write(i);
355        }
356
357        @Override
358        public String toString()
359        {
360            return stream.toString();
361        }
362
363        @Override
364        public void verify() throws IOException
365        {
366            if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST)
367            {
368                throw new IOException("HTTP Error " + connection.getResponseCode()
369                        + " " + connection.getResponseMessage());
370            }
371        }
372    }
373}