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     */
017    package org.apache.commons.configuration;
018    
019    import java.io.File;
020    import java.io.FileNotFoundException;
021    import java.io.FileOutputStream;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.io.OutputStream;
025    import java.net.HttpURLConnection;
026    import java.net.MalformedURLException;
027    import java.net.URL;
028    import java.net.URLConnection;
029    
030    import org.apache.commons.logging.Log;
031    import 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     */
039    public 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    }