blob: 485481d93a797016adbd052e9f6a44357745c9fe [file] [log] [blame]
/*
* 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.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.plugins.PluginAliases;
import org.apache.logging.log4j.core.net.UrlConnectionFactory;
import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory;
import org.apache.logging.log4j.core.util.AbstractWatcher;
import org.apache.logging.log4j.core.util.Source;
import org.apache.logging.log4j.core.util.Watcher;
import org.apache.logging.log4j.status.StatusLogger;
/**
*
*/
@Plugin(name = "http", category = Watcher.CATEGORY, elementType = Watcher.ELEMENT_TYPE, printObject = true)
@PluginAliases("https")
public class HttpWatcher extends AbstractWatcher {
private final Logger LOGGER = StatusLogger.getLogger();
private final SslConfiguration sslConfiguration;
private URL url;
private volatile long lastModifiedMillis;
private static final int NOT_MODIFIED = 304;
private static final int OK = 200;
private static final int BUF_SIZE = 1024;
private static final String HTTP = "http";
private static final String HTTPS = "https";
public HttpWatcher(final Configuration configuration, final Reconfigurable reconfigurable,
final List<ConfigurationListener> configurationListeners, long lastModifiedMillis) {
super(configuration, reconfigurable, configurationListeners);
sslConfiguration = SslConfigurationFactory.getSslConfiguration();
this.lastModifiedMillis = lastModifiedMillis;
}
@Override
public long getLastModified() {
return lastModifiedMillis;
}
@Override
public boolean isModified() {
return refreshConfiguration();
}
@Override
public void watching(Source source) {
if (!source.getURI().getScheme().equals(HTTP) && !source.getURI().getScheme().equals(HTTPS)) {
throw new IllegalArgumentException(
"HttpWatcher requires a url using the HTTP or HTTPS protocol, not " + source.getURI().getScheme());
}
try {
url = source.getURI().toURL();
} catch (MalformedURLException ex) {
throw new IllegalArgumentException("Invalid URL for HttpWatcher " + source.getURI(), ex);
}
super.watching(source);
}
@Override
public Watcher newWatcher(Reconfigurable reconfigurable, List<ConfigurationListener> listeners,
long lastModifiedMillis) {
HttpWatcher watcher = new HttpWatcher(getConfiguration(), reconfigurable, listeners, lastModifiedMillis);
if (getSource() != null) {
watcher.watching(getSource());
}
return watcher;
}
private boolean refreshConfiguration() {
try {
final HttpURLConnection urlConnection = UrlConnectionFactory.createConnection(url, lastModifiedMillis,
sslConfiguration);
urlConnection.connect();
try {
int code = urlConnection.getResponseCode();
switch (code) {
case NOT_MODIFIED: {
LOGGER.debug("Configuration Not Modified");
return false;
}
case OK: {
try (InputStream is = urlConnection.getInputStream()) {
ConfigurationSource configSource = getConfiguration().getConfigurationSource();
configSource.setData(readStream(is));
lastModifiedMillis = urlConnection.getLastModified();
configSource.setModifiedMillis(lastModifiedMillis);
LOGGER.debug("Content was modified for {}", url.toString());
return true;
} catch (final IOException e) {
try (InputStream es = urlConnection.getErrorStream()) {
LOGGER.info("Error accessing configuration at {}: {}", url, readStream(es));
} catch (final IOException ioe) {
LOGGER.error("Error accessing configuration at {}: {}", url, e.getMessage());
}
return false;
}
}
default: {
if (code < 0) {
LOGGER.info("Invalid response code returned");
} else {
LOGGER.info("Unexpected response code returned {}", code);
}
return false;
}
}
} catch (final IOException ioe) {
LOGGER.error("Error accessing configuration at {}: {}", url, ioe.getMessage());
}
} catch (final IOException ioe) {
LOGGER.error("Error connecting to configuration at {}: {}", url, ioe.getMessage());
}
return false;
}
private byte[] readStream(InputStream is) throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[BUF_SIZE];
int length;
while ((length = is.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
return result.toByteArray();
}
}