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