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