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    
018    package org.apache.logging.log4j.core.config;
019    
020    import java.io.ByteArrayInputStream;
021    import java.io.ByteArrayOutputStream;
022    import java.io.File;
023    import java.io.FileInputStream;
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.net.URL;
027    
028    import org.apache.logging.log4j.core.util.Assert;
029    
030    /**
031     * Represents the source for the logging configuration.
032     */
033    public 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    }