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