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
018package org.apache.logging.log4j.core.config;
019
020import java.io.ByteArrayInputStream;
021import java.io.ByteArrayOutputStream;
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.IOException;
025import java.io.InputStream;
026import java.net.URL;
027
028import org.apache.logging.log4j.core.util.Assert;
029
030/**
031 * Represents the source for the logging configuration.
032 */
033public class ConfigurationSource {
034    public static final ConfigurationSource NULL_SOURCE = new ConfigurationSource(new byte[0]);
035
036    private final File file;
037    private final URL url;
038    private final String location;
039    private final InputStream stream;
040    private final byte[] data;
041
042    /**
043     * Returns the contents of the specified {@code InputStream} as a byte array.
044     * 
045     * @param inputStream the stream to read
046     * @return the contents of the specified stream
047     * @throws IOException if a problem occurred reading from the stream
048     */
049    private static byte[] toByteArray(final InputStream inputStream) throws IOException {
050        final int buffSize = Math.max(4096, inputStream.available());
051        final ByteArrayOutputStream contents = new ByteArrayOutputStream(buffSize);
052        final byte[] buff = new byte[buffSize];
053
054        int length = inputStream.read(buff);
055        while (length > 0) {
056            contents.write(buff, 0, length);
057            length = inputStream.read(buff);
058        }
059        return contents.toByteArray();
060    }
061
062    /**
063     * Constructs a new {@code ConfigurationSource} with the specified input stream. Since the stream is the only source
064     * of data, this constructor makes a copy of the stream contents.
065     * 
066     * @param stream the input stream
067     * @throws IOException if an exception occurred reading from the specified stream
068     */
069    public ConfigurationSource(final InputStream stream) throws IOException {
070        this(toByteArray(stream));
071    }
072
073    private ConfigurationSource(final byte[] data) {
074        this.data = Assert.requireNonNull(data, "data is null");
075        this.stream = new ByteArrayInputStream(data);
076        this.file = null;
077        this.url = null;
078        this.location = null;
079    }
080
081    /**
082     * Constructs a new {@code ConfigurationSource} with the specified input stream that originated from the specified
083     * file.
084     * 
085     * @param stream the input stream
086     * @param file the file where the input stream originated
087     */
088    public ConfigurationSource(final InputStream stream, final File file) {
089        this.stream = Assert.requireNonNull(stream, "stream is null");
090        this.file = Assert.requireNonNull(file, "file is null");
091        this.location = file.getAbsolutePath();
092        this.url = null;
093        this.data = null;
094    }
095
096    /**
097     * Constructs a new {@code ConfigurationSource} with the specified input stream that originated from the specified
098     * url.
099     * 
100     * @param stream the input stream
101     * @param url the URL where the input stream originated
102     */
103    public ConfigurationSource(final InputStream stream, final URL url) {
104        this.stream = Assert.requireNonNull(stream, "stream is null");
105        this.url = Assert.requireNonNull(url, "URL is null");
106        this.location = url.toString();
107        this.file = null;
108        this.data = null;
109    }
110
111    /**
112     * Returns the file configuration source, or {@code null} if this configuration source is based on an URL or has
113     * neither a file nor an URL.
114     * 
115     * @return the configuration source file, or {@code null}
116     */
117    public File getFile() {
118        return file;
119    }
120
121    /**
122     * Returns the configuration source URL, or {@code null} if this configuration source is based on a file or has
123     * neither a file nor an URL.
124     * 
125     * @return the configuration source URL, or {@code null}
126     */
127    public URL getURL() {
128        return url;
129    }
130
131    /**
132     * Returns a string describing the configuration source file or URL, or {@code null} if this configuration source
133     * has neither a file nor an URL.
134     * 
135     * @return a string describing the configuration source file or URL, or {@code null}
136     */
137    public String getLocation() {
138        return location;
139    }
140
141    /**
142     * Returns the input stream that this configuration source was constructed with.
143     * 
144     * @return the input stream that this configuration source was constructed with.
145     */
146    public InputStream getInputStream() {
147        return stream;
148    }
149
150    /**
151     * Returns a new {@code ConfigurationSource} whose input stream is reset to the beginning.
152     * 
153     * @return a new {@code ConfigurationSource}
154     * @throws IOException if a problem occurred while opening the new input stream
155     */
156    public ConfigurationSource resetInputStream() throws IOException {
157        if (file != null) {
158            return new ConfigurationSource(new FileInputStream(file), file);
159        } else if (url != null) {
160            return new ConfigurationSource(url.openStream(), url);
161        } else {
162            return new ConfigurationSource(data);
163        }
164    }
165
166    @Override
167    public String toString() {
168        if (location != null) {
169            return location;
170        }
171        if (this == NULL_SOURCE) {
172            return "NULL_SOURCE";
173        }
174        final int length = data == null ? -1 : data.length;
175        return "stream (" + length + " bytes, unknown location)";
176    }
177}