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