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