/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache license, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */

package org.apache.logging.log4j.core.config;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Objects;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.net.UrlConnectionFactory;
import org.apache.logging.log4j.core.util.FileUtils;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.core.util.Source;
import org.apache.logging.log4j.util.LoaderUtil;

Represents the source for the logging configuration.
/** * Represents the source for the logging configuration. */
public class ConfigurationSource {
ConfigurationSource to use with Configurations that do not require a "real" configuration source.
/** * ConfigurationSource to use with Configurations that do not require a "real" configuration source. */
public static final ConfigurationSource NULL_SOURCE = new ConfigurationSource(new byte[0], null, 0); private static final String HTTPS = "https"; private static final String HTTP = "http"; private final File file; private final URL url; private final String location; private final InputStream stream; private volatile byte[] data; private volatile Source source = null; private final long lastModified; // Set when the configuration has been updated so reset can use it for the next lastModified timestamp. private volatile long modifiedMillis;
Constructs a new ConfigurationSource with the specified input stream that originated from the specified file.
Params:
  • stream – the input stream
  • file – the file where the input stream originated
/** * Constructs a new {@code ConfigurationSource} with the specified input stream that originated from the specified * file. * * @param stream the input stream * @param file the file where the input stream originated */
public ConfigurationSource(final InputStream stream, final File file) { this.stream = Objects.requireNonNull(stream, "stream is null"); this.file = Objects.requireNonNull(file, "file is null"); this.location = file.getAbsolutePath(); this.url = null; this.data = null; long modified = 0; try { modified = file.lastModified(); } catch (Exception ex) { // There is a problem with the file. It will be handled somewhere else. } this.lastModified = modified; }
Constructs a new ConfigurationSource with the specified input stream that originated from the specified url.
Params:
  • stream – the input stream
  • url – the URL where the input stream originated
/** * Constructs a new {@code ConfigurationSource} with the specified input stream that originated from the specified * url. * * @param stream the input stream * @param url the URL where the input stream originated */
public ConfigurationSource(final InputStream stream, final URL url) { this.stream = Objects.requireNonNull(stream, "stream is null"); this.url = Objects.requireNonNull(url, "URL is null"); this.location = url.toString(); this.file = null; this.data = null; this.lastModified = 0; }
Constructs a new ConfigurationSource with the specified input stream that originated from the specified url.
Params:
  • stream – the input stream
  • url – the URL where the input stream originated
  • lastModified – when the source was last modified.
/** * Constructs a new {@code ConfigurationSource} with the specified input stream that originated from the specified * url. * * @param stream the input stream * @param url the URL where the input stream originated * @param lastModified when the source was last modified. */
public ConfigurationSource(final InputStream stream, final URL url, long lastModified) { this.stream = Objects.requireNonNull(stream, "stream is null"); this.url = Objects.requireNonNull(url, "URL is null"); this.location = url.toString(); this.file = null; this.data = null; this.lastModified = lastModified; }
Constructs a new ConfigurationSource with the specified input stream. Since the stream is the only source of data, this constructor makes a copy of the stream contents.
Params:
  • stream – the input stream
Throws:
  • IOException – if an exception occurred reading from the specified stream
/** * Constructs a new {@code ConfigurationSource} with the specified input stream. Since the stream is the only source * of data, this constructor makes a copy of the stream contents. * * @param stream the input stream * @throws IOException if an exception occurred reading from the specified stream */
public ConfigurationSource(final InputStream stream) throws IOException { this(toByteArray(stream), null, 0); } public ConfigurationSource(final Source source, final byte[] data, long lastModified) throws IOException { Objects.requireNonNull(source, "source is null"); this.data = Objects.requireNonNull(data, "data is null"); this.stream = new ByteArrayInputStream(data); this.file = source.getFile(); this.url = source.getURI().toURL(); this.location = source.getLocation(); this.lastModified = lastModified; } private ConfigurationSource(final byte[] data, final URL url, long lastModified) { Objects.requireNonNull(data, "data is null"); this.stream = new ByteArrayInputStream(data); this.file = null; this.url = url; this.location = null; this.lastModified = lastModified; }
Returns the contents of the specified InputStream as a byte array.
Params:
  • inputStream – the stream to read
Throws:
  • IOException – if a problem occurred reading from the stream
Returns:the contents of the specified stream
/** * Returns the contents of the specified {@code InputStream} as a byte array. * * @param inputStream the stream to read * @return the contents of the specified stream * @throws IOException if a problem occurred reading from the stream */
private static byte[] toByteArray(final InputStream inputStream) throws IOException { final int buffSize = Math.max(4096, inputStream.available()); final ByteArrayOutputStream contents = new ByteArrayOutputStream(buffSize); final byte[] buff = new byte[buffSize]; int length = inputStream.read(buff); while (length > 0) { contents.write(buff, 0, length); length = inputStream.read(buff); } return contents.toByteArray(); }
Returns the file configuration source, or null if this configuration source is based on an URL or has neither a file nor an URL.
Returns:the configuration source file, or null
/** * Returns the file configuration source, or {@code null} if this configuration source is based on an URL or has * neither a file nor an URL. * * @return the configuration source file, or {@code null} */
public File getFile() { return file; }
Returns the configuration source URL, or null if this configuration source is based on a file or has neither a file nor an URL.
Returns:the configuration source URL, or null
/** * Returns the configuration source URL, or {@code null} if this configuration source is based on a file or has * neither a file nor an URL. * * @return the configuration source URL, or {@code null} */
public URL getURL() { return url; } public void setSource(Source source) { this.source = source; } public void setData(byte[] data) { this.data = data; } public void setModifiedMillis(long modifiedMillis) { this.modifiedMillis = modifiedMillis; }
Returns a URI representing the configuration resource or null if it cannot be determined.
Returns:The URI.
/** * Returns a URI representing the configuration resource or null if it cannot be determined. * @return The URI. */
public URI getURI() { URI sourceURI = null; if (url != null) { try { sourceURI = url.toURI(); } catch (final URISyntaxException ex) { /* Ignore the exception */ } } if (sourceURI == null && file != null) { sourceURI = file.toURI(); } if (sourceURI == null && location != null) { try { sourceURI = new URI(location); } catch (final URISyntaxException ex) { // Assume the scheme was missing. try { sourceURI = new URI("file://" + location); } catch (final URISyntaxException uriEx) { /* Ignore the exception */ } } } return sourceURI; }
Returns the time the resource was last modified or 0 if it is not available.
Returns:the last modified time of the resource.
/** * Returns the time the resource was last modified or 0 if it is not available. * @return the last modified time of the resource. */
public long getLastModified() { return lastModified; }
Returns a string describing the configuration source file or URL, or null if this configuration source has neither a file nor an URL.
Returns:a string describing the configuration source file or URL, or null
/** * Returns a string describing the configuration source file or URL, or {@code null} if this configuration source * has neither a file nor an URL. * * @return a string describing the configuration source file or URL, or {@code null} */
public String getLocation() { return location; }
Returns the input stream that this configuration source was constructed with.
Returns:the input stream that this configuration source was constructed with.
/** * Returns the input stream that this configuration source was constructed with. * * @return the input stream that this configuration source was constructed with. */
public InputStream getInputStream() { return stream; }
Returns a new ConfigurationSource whose input stream is reset to the beginning.
Throws:
  • IOException – if a problem occurred while opening the new input stream
Returns:a new ConfigurationSource
/** * Returns a new {@code ConfigurationSource} whose input stream is reset to the beginning. * * @return a new {@code ConfigurationSource} * @throws IOException if a problem occurred while opening the new input stream */
public ConfigurationSource resetInputStream() throws IOException { if (source != null) { return new ConfigurationSource(source, data, this.lastModified); } else if (file != null) { return new ConfigurationSource(new FileInputStream(file), file); } else if (url != null && data != null) { // Creates a ConfigurationSource without accessing the URL since the data was provided. return new ConfigurationSource(data, url, modifiedMillis == 0 ? lastModified : modifiedMillis); } else if (url != null) { return fromUri(getURI()); } else if (data != null) { return new ConfigurationSource(data, null, lastModified); } return null; } @Override public String toString() { if (location != null) { return location; } if (this == NULL_SOURCE) { return "NULL_SOURCE"; } final int length = data == null ? -1 : data.length; return "stream (" + length + " bytes, unknown location)"; }
Loads the configuration from a URI.
Params:
  • configLocation – A URI representing the location of the configuration.
Returns:The ConfigurationSource for the configuration.
/** * Loads the configuration from a URI. * @param configLocation A URI representing the location of the configuration. * @return The ConfigurationSource for the configuration. */
public static ConfigurationSource fromUri(final URI configLocation) { final File configFile = FileUtils.fileFromUri(configLocation); if (configFile != null && configFile.exists() && configFile.canRead()) { try { return new ConfigurationSource(new FileInputStream(configFile), configFile); } catch (final FileNotFoundException ex) { ConfigurationFactory.LOGGER.error("Cannot locate file {}", configLocation.getPath(), ex); } } if (ConfigurationFactory.isClassLoaderUri(configLocation)) { final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); final String path = ConfigurationFactory.extractClassLoaderUriPath(configLocation); final ConfigurationSource source = fromResource(path, loader); if (source != null) { return source; } } if (!configLocation.isAbsolute()) { // LOG4J2-704 avoid confusing error message thrown by uri.toURL() ConfigurationFactory.LOGGER.error("File not found in file system or classpath: {}", configLocation.toString()); return null; } try { URL url = configLocation.toURL(); URLConnection urlConnection = UrlConnectionFactory.createConnection(url); InputStream is = urlConnection.getInputStream(); long lastModified = urlConnection.getLastModified(); return new ConfigurationSource(is, configLocation.toURL(), lastModified); } catch (final MalformedURLException ex) { ConfigurationFactory.LOGGER.error("Invalid URL {}", configLocation.toString(), ex); } catch (final Exception ex) { ConfigurationFactory.LOGGER.error("Unable to access {}", configLocation.toString(), ex); } return null; }
Retrieves the configuration via the ClassLoader.
Params:
  • resource – The resource to load.
  • loader – The default ClassLoader to use.
Returns:The ConfigurationSource for the configuration.
/** * Retrieves the configuration via the ClassLoader. * @param resource The resource to load. * @param loader The default ClassLoader to use. * @return The ConfigurationSource for the configuration. */
public static ConfigurationSource fromResource(final String resource, final ClassLoader loader) { final URL url = Loader.getResource(resource, loader); if (url == null) { return null; } InputStream is = null; try { is = url.openStream(); } catch (final IOException ioe) { ConfigurationFactory.LOGGER.catching(Level.DEBUG, ioe); return null; } if (is == null) { return null; } if (FileUtils.isFile(url)) { try { return new ConfigurationSource(is, FileUtils.fileFromUri(url.toURI())); } catch (final URISyntaxException ex) { // Just ignore the exception. ConfigurationFactory.LOGGER.catching(Level.DEBUG, ex); } } return new ConfigurationSource(is, url); } }