blob: 4f487f51290b1a5434b3c10b2ac2d7130b7addaf [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.server;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LogEventListener;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.xml.XmlConfiguration;
import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory;
import org.apache.logging.log4j.core.util.BasicCommandLineArguments;
import org.apache.logging.log4j.core.util.Log4jThread;
import org.apache.logging.log4j.core.util.picocli.CommandLine.Option;
import org.apache.logging.log4j.util.Strings;
/**
* Abstract socket server for TCP and UDP implementations.
*
* @param <T>
* The kind of input stream read
*
* TODO Make a LifeCycle
*/
public abstract class AbstractSocketServer<T extends InputStream> extends LogEventListener implements Runnable {
protected static class CommandLineArguments extends BasicCommandLineArguments {
@Option(names = { "--config", "-c" }, description = "Log4j configuration file location (path or URL).")
private String configLocation;
@Option(names = { "--interactive", "-i" }, description = "Accepts commands on standard input (\"exit\" is the only command).")
private boolean interactive;
@Option(names = { "--port", "-p" }, description = "The server port number, or 0 to automatically allocate a port number.")
private int port;
@Option(names = { "--localbindaddress", "-a" }, description = "Server socket local bind address.")
private InetAddress localBindAddress;
@Option(names = {"--classes", "-C"}, description = "Additional classes to allow deserialization")
private List<String> allowedClasses;
String getConfigLocation() {
return configLocation;
}
int getPort() {
return port;
}
protected boolean isInteractive() {
return interactive;
}
void setConfigLocation(final String configLocation) {
this.configLocation = configLocation;
}
void setInteractive(final boolean interactive) {
this.interactive = interactive;
}
void setPort(final int port) {
this.port = port;
}
InetAddress getLocalBindAddress() {
return localBindAddress;
}
void setLocalBindAddress(final InetAddress localBindAddress) {
this.localBindAddress = localBindAddress;
}
List<String> getAllowedClasses() {
return allowedClasses == null ? Collections.<String>emptyList() : allowedClasses;
}
void setAllowedClasses(final List<String> allowedClasses) {
this.allowedClasses = allowedClasses;
}
}
/**
* Factory that creates a Configuration for the server.
*/
protected static class ServerConfigurationFactory extends XmlConfigurationFactory {
private final String path;
public ServerConfigurationFactory(final String path) {
this.path = path;
}
@Override
public Configuration getConfiguration(final LoggerContext loggerContext, final String name,
final URI configLocation) {
if (Strings.isNotEmpty(path)) {
File file = null;
ConfigurationSource source = null;
try {
file = new File(path);
final FileInputStream is = new FileInputStream(file);
source = new ConfigurationSource(is, file);
} catch (final FileNotFoundException ignored) {
// Ignore this error
}
if (source == null) {
try {
final URL url = new URL(path);
source = new ConfigurationSource(url.openStream(), url);
} catch (final IOException ignored) {
// Ignore this error
}
}
try {
if (source != null) {
return new XmlConfiguration(loggerContext, source);
}
} catch (final Exception ignored) {
// Ignore this error.
}
System.err.println("Unable to process configuration at " + path + ", using default.");
}
return super.getConfiguration(loggerContext, name, configLocation);
}
}
protected static final int MAX_PORT = 65534;
private volatile boolean active = true;
protected final LogEventBridge<T> logEventInput;
protected final Logger logger;
/**
* Creates a new socket server.
*
* @param port
* listen to this port
* @param logEventInput
* Use this input to read log events.
*/
public AbstractSocketServer(final int port, final LogEventBridge<T> logEventInput) {
this.logger = LogManager.getLogger(this.getClass().getName() + '.' + port);
this.logEventInput = Objects.requireNonNull(logEventInput, "LogEventInput");
}
protected boolean isActive() {
return this.active;
}
protected void setActive(final boolean isActive) {
this.active = isActive;
}
/**
* Start this server in a new thread.
*
* @return the new thread that running this server.
*/
public Thread startNewThread() {
final Thread thread = new Log4jThread(this);
thread.start();
return thread;
}
public abstract void shutdown() throws Exception;
public void awaitTermination(final Thread serverThread) throws Exception {
final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
final String line = reader.readLine();
if (line == null
|| line.equalsIgnoreCase("quit")
|| line.equalsIgnoreCase("stop")
|| line.equalsIgnoreCase("exit")) {
this.shutdown();
serverThread.join();
break;
}
}
}
}