Move log4j-server to Tools repo
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..627e878
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,27 @@
+# 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.
+
+.project
+.idea
+**/*.iml
+**/target
+target/
+.settings
+.classpath
+.cache-main
+.cache-tests
+velocity.log
+felix-cache/
+bin/
diff --git a/log4j-server/pom.xml b/log4j-server/pom.xml
new file mode 100644
index 0000000..5e3c98d
--- /dev/null
+++ b/log4j-server/pom.xml
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-tools</artifactId>
+ <version>2.8.3-SNAPSHOT</version>
+ <relativePath>../</relativePath>
+ </parent>
+ <artifactId>log4j-server</artifactId>
+ <packaging>jar</packaging>
+ <name>Apache Log4j Server components</name>
+ <description>The Apache Log4j server components</description>
+ <properties>
+ <log4jParentDir>${basedir}/..</log4jParentDir>
+ <projectDir>/log4j-server</projectDir>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <!-- Command line parsing -->
+ <dependency>
+ <groupId>com.beust</groupId>
+ <artifactId>jcommander</artifactId>
+ </dependency>
+ <!-- Used for JMS server (needs an implementation of course) -->
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+ <!-- Required for JSON support -->
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+ <!-- Required for XML support -->
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-xml</artifactId>
+ </dependency>
+ <!-- POM for jackson-dataformat-xml 2.8.3 depends on woodstox-core 5.0.2 -->
+ <dependency>
+ <groupId>com.fasterxml.woodstox</groupId>
+ <artifactId>woodstox-core</artifactId>
+ <version>5.0.2</version>
+ </dependency>
+
+ <!-- TEST DEPENDENCIES -->
+
+ <!-- Pull in useful test classes from API -->
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <!-- Include the standard NOTICE and LICENSE -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-remote-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>process</goal>
+ </goals>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Export-Package>org.apache.logging.log4j.core.net.*</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-changes-plugin</artifactId>
+ <version>${changes.plugin.version}</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>changes-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <issueLinkTemplate>%URL%/show_bug.cgi?id=%ISSUE%</issueLinkTemplate>
+ <useJql>true</useJql>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>${checkstyle.plugin.version}</version>
+ <configuration>
+ <!--<propertiesLocation>${vfs.parent.dir}/checkstyle.properties</propertiesLocation> -->
+ <configLocation>${log4jParentDir}/checkstyle.xml</configLocation>
+ <suppressionsLocation>${log4jParentDir}/checkstyle-suppressions.xml</suppressionsLocation>
+ <enableRulesSummary>false</enableRulesSummary>
+ <propertyExpansion>basedir=${basedir}</propertyExpansion>
+ <propertyExpansion>licensedir=${log4jParentDir}/checkstyle-header.txt</propertyExpansion>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>${javadoc.plugin.version}</version>
+ <configuration>
+ <bottom><![CDATA[<p align="center">Copyright © {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br />
+ Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo,
+ and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>]]></bottom>
+ <!-- module link generation is completely broken in the javadoc plugin for a multi-module non-aggregating
+ project -->
+ <detectOfflineLinks>false</detectOfflineLinks>
+ <linksource>true</linksource>
+ </configuration>
+ <reportSets>
+ <reportSet>
+ <id>non-aggregate</id>
+ <reports>
+ <report>javadoc</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>${findbugs.plugin.version}</version>
+ <configuration>
+ <fork>true</fork>
+ <jvmArgs>-Duser.language=en</jvmArgs>
+ <threshold>Normal</threshold>
+ <effort>Default</effort>
+ <excludeFilterFile>${log4jParentDir}/findbugs-exclude-filter.xml</excludeFilterFile>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jxr-plugin</artifactId>
+ <version>${jxr.plugin.version}</version>
+ <reportSets>
+ <reportSet>
+ <id>non-aggregate</id>
+ <reports>
+ <report>jxr</report>
+ </reports>
+ </reportSet>
+ <reportSet>
+ <id>aggregate</id>
+ <reports>
+ <report>aggregate</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <version>${pmd.plugin.version}</version>
+ <configuration>
+ <targetJdk>${maven.compiler.target}</targetJdk>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+</project>
+
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractLogEventBridge.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractLogEventBridge.java
new file mode 100644
index 0000000..5e368cb
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractLogEventBridge.java
@@ -0,0 +1,44 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Abstract class for implementations of {@link LogEventBridge}.
+ *
+ * @param <T>
+ * The kind of input stream read
+ */
+public abstract class AbstractLogEventBridge<T extends InputStream> implements LogEventBridge<T> {
+
+ protected static final int END = -1;
+
+ protected static final Logger logger = StatusLogger.getLogger();
+
+ // The default is to return the same object as given.
+ @SuppressWarnings("unchecked")
+ @Override
+ public T wrapStream(final InputStream inputStream) throws IOException {
+ return (T) inputStream;
+ }
+
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractSocketServer.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractSocketServer.java
new file mode 100644
index 0000000..aff7eb1
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractSocketServer.java
@@ -0,0 +1,222 @@
+/*
+ * 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 com.beust.jcommander.Parameter;
+import com.beust.jcommander.validators.PositiveInteger;
+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.InetAddressConverter;
+import org.apache.logging.log4j.core.util.Log4jThread;
+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 {
+
+ @Parameter(names = { "--config", "-c" }, description = "Log4j configuration file location (path or URL).")
+ private String configLocation;
+
+ @Parameter(names = { "--interactive",
+ "-i" }, description = "Accepts commands on standard input (\"exit\" is the only command).")
+ private boolean interactive;
+
+ @Parameter(names = { "--port",
+ "-p" }, validateWith = PositiveInteger.class, description = "Server socket port.")
+ private int port;
+
+ @Parameter(names = { "--localbindaddress",
+ "-a" }, converter = InetAddressConverter.class, description = "Server socket local bind address.")
+ private InetAddress localBindAddress;
+
+ @Parameter(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;
+ }
+ }
+ }
+
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/InputStreamLogEventBridge.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/InputStreamLogEventBridge.java
new file mode 100644
index 0000000..68ec791
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/InputStreamLogEventBridge.java
@@ -0,0 +1,103 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.util.Strings;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+
+/**
+ * Reads and logs {@link LogEvent}s from an {@link InputStream}.
+ */
+public abstract class InputStreamLogEventBridge extends AbstractLogEventBridge<InputStream> {
+
+ private final int bufferSize;
+
+ private final Charset charset;
+
+ private final String eventEndMarker;
+
+ private final ObjectReader objectReader;
+
+ public InputStreamLogEventBridge(final ObjectMapper mapper, final int bufferSize, final Charset charset, final String eventEndMarker) {
+ this.bufferSize = bufferSize;
+ this.charset = charset;
+ this.eventEndMarker = eventEndMarker;
+ this.objectReader = mapper.readerFor(Log4jLogEvent.class);
+ }
+
+ abstract protected int[] getEventIndices(final String text, int beginIndex);
+
+ @Override
+ public void logEvents(final InputStream inputStream, final LogEventListener logEventListener) throws IOException {
+ String workingText = Strings.EMPTY;
+ try {
+ // Allocate buffer once
+ final byte[] buffer = new byte[bufferSize];
+ String textRemains = workingText = Strings.EMPTY;
+ while (true) {
+ // Process until the stream is EOF.
+ final int streamReadLength = inputStream.read(buffer);
+ if (streamReadLength == END) {
+ // The input stream is EOF
+ break;
+ }
+ final String text = workingText = textRemains + new String(buffer, 0, streamReadLength, charset);
+ int beginIndex = 0;
+ while (true) {
+ // Extract and log all XML events in the buffer
+ final int[] pair = getEventIndices(text, beginIndex);
+ final int eventStartMarkerIndex = pair[0];
+ if (eventStartMarkerIndex < 0) {
+ // No more events or partial XML only in the buffer.
+ // Save the unprocessed string part
+ textRemains = text.substring(beginIndex);
+ break;
+ }
+ final int eventEndMarkerIndex = pair[1];
+ if (eventEndMarkerIndex > 0) {
+ final int eventEndXmlIndex = eventEndMarkerIndex + eventEndMarker.length();
+ final String textEvent = workingText = text.substring(eventStartMarkerIndex, eventEndXmlIndex);
+ final LogEvent logEvent = unmarshal(textEvent);
+ logEventListener.log(logEvent);
+ beginIndex = eventEndXmlIndex;
+ } else {
+ // No more events or partial XML only in the buffer.
+ // Save the unprocessed string part
+ textRemains = text.substring(beginIndex);
+ break;
+ }
+ }
+ }
+ } catch (final IOException ex) {
+ logger.error(workingText, ex);
+ }
+ }
+
+ protected Log4jLogEvent unmarshal(final String jsonEvent) throws IOException {
+ return this.objectReader.readValue(jsonEvent);
+ }
+
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/JmsServer.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/JmsServer.java
new file mode 100644
index 0000000..b673c06
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/JmsServer.java
@@ -0,0 +1,148 @@
+/*
+ * 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.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.ObjectMessage;
+
+import org.apache.logging.log4j.LoggingException;
+import org.apache.logging.log4j.core.AbstractLifeCycle;
+import org.apache.logging.log4j.core.LifeCycle2;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+import org.apache.logging.log4j.core.appender.mom.JmsManager;
+import org.apache.logging.log4j.core.net.JndiManager;
+
+/**
+ * LogEventListener server that receives LogEvents over a JMS {@link javax.jms.Destination}.
+ *
+ * @since 2.1
+ */
+public class JmsServer extends LogEventListener implements MessageListener, LifeCycle2 {
+
+ private final AtomicReference<State> state = new AtomicReference<>(State.INITIALIZED);
+ private final JmsManager jmsManager;
+ private MessageConsumer messageConsumer;
+
+ public JmsServer(final String connectionFactoryBindingName,
+ final String destinationBindingName,
+ final String username,
+ final String password) {
+ final String managerName = JmsServer.class.getName() + '@' + JmsServer.class.hashCode();
+ final JndiManager jndiManager = JndiManager.getDefaultManager(managerName);
+ jmsManager = JmsManager.getJmsManager(managerName, jndiManager, connectionFactoryBindingName,
+ destinationBindingName, username, password);
+ }
+
+ @Override
+ public State getState() {
+ return state.get();
+ }
+
+ @Override
+ public void onMessage(final Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final Object body = ((ObjectMessage) message).getObject();
+ if (body instanceof LogEvent) {
+ log((LogEvent) body);
+ } else {
+ LOGGER.warn("Expected ObjectMessage to contain LogEvent. Got type {} instead.", body.getClass());
+ }
+ } else {
+ LOGGER.warn("Received message of type {} and JMSType {} which cannot be handled.", message.getClass(),
+ message.getJMSType());
+ }
+ } catch (final JMSException e) {
+ LOGGER.catching(e);
+ }
+ }
+
+ @Override
+ public void initialize() {
+ }
+
+ @Override
+ public void start() {
+ if (state.compareAndSet(State.INITIALIZED, State.STARTING)) {
+ try {
+ messageConsumer = jmsManager.createMessageConsumer();
+ messageConsumer.setMessageListener(this);
+ } catch (final JMSException e) {
+ throw new LoggingException(e);
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+ stop(AbstractLifeCycle.DEFAULT_STOP_TIMEOUT, AbstractLifeCycle.DEFAULT_STOP_TIMEUNIT);
+ }
+
+ @Override
+ public boolean stop(final long timeout, final TimeUnit timeUnit) {
+ boolean stopped = true;
+ try {
+ messageConsumer.close();
+ } catch (final JMSException e) {
+ LOGGER.debug("Exception closing {}", messageConsumer, e);
+ stopped = false;
+ }
+ return stopped && jmsManager.stop(timeout, timeUnit);
+ }
+
+ @Override
+ public boolean isStarted() {
+ return state.get() == State.STARTED;
+ }
+
+ @Override
+ public boolean isStopped() {
+ return state.get() == State.STOPPED;
+ }
+
+ /**
+ * Starts and runs this server until the user types "exit" into standard input.
+ *
+ * @throws IOException
+ * @since 2.6
+ */
+ public void run() throws IOException {
+ this.start();
+ System.out.println("Type \"exit\" to quit.");
+ final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, Charset.defaultCharset()));
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it does not exit due to daemon threads.");
+ this.stop();
+ return;
+ }
+ }
+ }
+
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/JsonInputStreamLogEventBridge.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/JsonInputStreamLogEventBridge.java
new file mode 100644
index 0000000..6a06ae4
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/JsonInputStreamLogEventBridge.java
@@ -0,0 +1,90 @@
+/*
+ * 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.InputStream;
+import java.nio.charset.Charset;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.jackson.Log4jJsonObjectMapper;
+import org.apache.logging.log4j.util.Chars;
+
+/**
+ * Reads and logs JSON {@link LogEvent}s from an {@link InputStream}..
+ */
+public class JsonInputStreamLogEventBridge extends InputStreamLogEventBridge {
+
+ private static final int[] END_PAIR = new int[] { END, END };
+ private static final char EVENT_END_MARKER = '}';
+ private static final char EVENT_START_MARKER = '{';
+ private static final char JSON_ESC = '\\';
+ private static final char JSON_STR_DELIM = Chars.DQUOTE;
+ private static final boolean THREAD_CONTEXT_MAP_AS_LIST = false;
+
+ public JsonInputStreamLogEventBridge() {
+ this(1024, Charset.defaultCharset());
+ }
+
+ public JsonInputStreamLogEventBridge(final int bufferSize, final Charset charset) {
+ super(new Log4jJsonObjectMapper(THREAD_CONTEXT_MAP_AS_LIST, true), bufferSize, charset,
+ String.valueOf(EVENT_END_MARKER));
+ }
+
+ @Override
+ protected int[] getEventIndices(final String text, final int beginIndex) {
+ // Scan the text for the end of the next JSON object.
+ final int start = text.indexOf(EVENT_START_MARKER, beginIndex);
+ if (start == END) {
+ return END_PAIR;
+ }
+ final char[] charArray = text.toCharArray();
+ int stack = 0;
+ boolean inStr = false;
+ boolean inEsc = false;
+ for (int i = start; i < charArray.length; i++) {
+ final char c = charArray[i];
+ if (inEsc) {
+ // Skip this char and continue
+ inEsc = false;
+ } else {
+ switch (c) {
+ case EVENT_START_MARKER:
+ if (!inStr) {
+ stack++;
+ }
+ break;
+ case EVENT_END_MARKER:
+ if (!inStr) {
+ stack--;
+ }
+ break;
+ case JSON_STR_DELIM:
+ inStr = !inStr;
+ break;
+ case JSON_ESC:
+ inEsc = true;
+ break;
+ }
+ if (stack == 0) {
+ return new int[] { start, i };
+ }
+ }
+ }
+ return END_PAIR;
+ }
+
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/LogEventBridge.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/LogEventBridge.java
new file mode 100644
index 0000000..785c365
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/LogEventBridge.java
@@ -0,0 +1,57 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Reads {@link LogEvent}s from the given input stream and logs them as they are discovered on the given logger.
+ *
+ * <p>
+ * Should be stateless.
+ * </p>
+ *
+ * @param <T>
+ * The kind of {@link InputStream} to wrap and read.
+ */
+public interface LogEventBridge<T extends InputStream> {
+
+ /**
+ * Reads {@link LogEvent}s from the given input stream and logs them as they are discovered on the given logger.
+ *
+ * @param inputStream
+ * the input stream to read
+ * @param logEventListener
+ * TODO
+ * @throws IOException
+ */
+ void logEvents(T inputStream, LogEventListener logEventListener) throws IOException;
+
+ /**
+ * Wraps the given stream if needed.
+ *
+ * @param inputStream
+ * the stream to wrap
+ * @return the wrapped stream or the given stream.
+ * @throws IOException
+ */
+ T wrapStream(InputStream inputStream) throws IOException;
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/ObjectInputStreamLogEventBridge.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/ObjectInputStreamLogEventBridge.java
new file mode 100644
index 0000000..ddd2e26
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/ObjectInputStreamLogEventBridge.java
@@ -0,0 +1,64 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+import org.apache.logging.log4j.core.util.FilteredObjectInputStream;
+
+/**
+ * Reads and logs serialized {@link LogEvent} objects from an {@link ObjectInputStream}.
+ */
+public class ObjectInputStreamLogEventBridge extends AbstractLogEventBridge<ObjectInputStream> {
+
+ private final List<String> allowedClasses;
+
+ public ObjectInputStreamLogEventBridge() {
+ this(Collections.<String>emptyList());
+ }
+
+ /**
+ * Constructs an ObjectInputStreamLogEventBridge with additional allowed classes to deserialize.
+ *
+ * @param allowedClasses class names to also allow for deserialization
+ * @since 2.8.2
+ */
+ public ObjectInputStreamLogEventBridge(final List<String> allowedClasses) {
+ this.allowedClasses = allowedClasses;
+ }
+
+ @Override
+ public void logEvents(final ObjectInputStream inputStream, final LogEventListener logEventListener)
+ throws IOException {
+ try {
+ logEventListener.log((LogEvent) inputStream.readObject());
+ } catch (final ClassNotFoundException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public ObjectInputStream wrapStream(final InputStream inputStream) throws IOException {
+ return new FilteredObjectInputStream(inputStream, allowedClasses);
+ }
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/SecureTcpSocketServer.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/SecureTcpSocketServer.java
new file mode 100644
index 0000000..81c9d64
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/SecureTcpSocketServer.java
@@ -0,0 +1,37 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+
+import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
+
+/**
+ * Listens for events over a secure socket connection (SSL/TLS).
+ *
+ * @param <T>
+ * The kind of input stream read
+ */
+public class SecureTcpSocketServer<T extends InputStream> extends TcpSocketServer<T> {
+
+ public SecureTcpSocketServer(final int port, final LogEventBridge<T> logEventInput,
+ final SslConfiguration sslConfig) throws IOException {
+ super(port, logEventInput, sslConfig.getSslServerSocketFactory().createServerSocket(port));
+ }
+
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/TcpSocketServer.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/TcpSocketServer.java
new file mode 100644
index 0000000..9a5521c
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/TcpSocketServer.java
@@ -0,0 +1,333 @@
+/*
+ * 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.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.OptionalDataException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.validators.PositiveInteger;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.util.BasicCommandLineArguments;
+import org.apache.logging.log4j.core.util.Closer;
+import org.apache.logging.log4j.core.util.Log4jThread;
+import org.apache.logging.log4j.message.EntryMessage;
+
+/**
+ * Listens for Log4j events on a TCP server socket and passes them on to Log4j.
+ *
+ * @param <T>
+ * The kind of input stream read
+ * @see #main(String[])
+ */
+public class TcpSocketServer<T extends InputStream> extends AbstractSocketServer<T> {
+
+ protected static class CommandLineArguments extends AbstractSocketServer.CommandLineArguments {
+
+ @Parameter(names = { "--backlog",
+ "-b" }, validateWith = PositiveInteger.class, description = "Server socket backlog.")
+ // Same default as ServerSocket
+ private int backlog = 50;
+
+ int getBacklog() {
+ return backlog;
+ }
+
+ void setBacklog(final int backlog) {
+ this.backlog = backlog;
+ }
+
+ }
+
+ /**
+ * Thread that processes the events.
+ */
+ private class SocketHandler extends Log4jThread {
+
+ private final T inputStream;
+
+ private volatile boolean shutdown = false;
+
+ public SocketHandler(final Socket socket) throws IOException {
+ this.inputStream = logEventInput.wrapStream(socket.getInputStream());
+ }
+
+ @Override
+ public void run() {
+ final EntryMessage entry = logger.traceEntry();
+ boolean closed = false;
+ try {
+ try {
+ while (!shutdown) {
+ logEventInput.logEvents(inputStream, TcpSocketServer.this);
+ }
+ } catch (final EOFException e) {
+ closed = true;
+ } catch (final OptionalDataException e) {
+ logger.error("OptionalDataException eof=" + e.eof + " length=" + e.length, e);
+ } catch (final IOException e) {
+ logger.error("IOException encountered while reading from socket", e);
+ }
+ if (!closed) {
+ Closer.closeSilently(inputStream);
+ }
+ } finally {
+ handlers.remove(Long.valueOf(getId()));
+ }
+ logger.traceExit(entry);
+ }
+
+ public void shutdown() {
+ this.shutdown = true;
+ interrupt();
+ }
+ }
+
+ /**
+ * Creates a socket server that reads JSON log events.
+ *
+ * @param port
+ * the port to listen
+ * @return a new a socket server
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ */
+ public static TcpSocketServer<InputStream> createJsonSocketServer(final int port) throws IOException {
+ LOGGER.entry("createJsonSocketServer", port);
+ final TcpSocketServer<InputStream> socketServer = new TcpSocketServer<>(port, new JsonInputStreamLogEventBridge());
+ return LOGGER.exit(socketServer);
+ }
+
+ /**
+ * Creates a socket server that reads serialized log events.
+ *
+ * @param port
+ * the port to listen
+ * @return a new a socket server
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ */
+ public static TcpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port) throws IOException {
+ LOGGER.entry(port);
+ final TcpSocketServer<ObjectInputStream> socketServer = new TcpSocketServer<>(port, new ObjectInputStreamLogEventBridge());
+ return LOGGER.exit(socketServer);
+ }
+
+ /**
+ * Creates a socket server that reads serialized log events.
+ *
+ * @param port the port to listen
+ * @param localBindAddress The server socket's local bin address
+ * @return a new a socket server
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ * @since 2.7
+ */
+ public static TcpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port, final int backlog,
+ final InetAddress localBindAddress) throws IOException {
+ return createSerializedSocketServer(port, backlog, localBindAddress, Collections.<String>emptyList());
+ }
+
+ /**
+ * Creates a socket server that reads serialized log events.
+ *
+ * @param port the port to listen
+ * @param localBindAddress The server socket's local bin address
+ * @param allowedClasses additional class names to allow for deserialization
+ * @return a new a socket server
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ * @since 2.8.2
+ */
+ public static TcpSocketServer<ObjectInputStream> createSerializedSocketServer(
+ final int port, final int backlog, final InetAddress localBindAddress, final List<String> allowedClasses
+ ) throws IOException {
+ LOGGER.entry(port);
+ final TcpSocketServer<ObjectInputStream> socketServer = new TcpSocketServer<>(port, backlog, localBindAddress,
+ new ObjectInputStreamLogEventBridge(allowedClasses));
+ return LOGGER.exit(socketServer);
+ }
+
+ /**
+ * Creates a socket server that reads XML log events.
+ *
+ * @param port
+ * the port to listen
+ * @return a new a socket server
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ */
+ public static TcpSocketServer<InputStream> createXmlSocketServer(final int port) throws IOException {
+ LOGGER.entry(port);
+ final TcpSocketServer<InputStream> socketServer = new TcpSocketServer<>(port, new XmlInputStreamLogEventBridge());
+ return LOGGER.exit(socketServer);
+ }
+
+ /**
+ * Main startup for the server. Run with "--help" for to print command line help on the console.
+ *
+ * @param args
+ * The command line arguments.
+ * @throws Exception
+ * if an error occurs.
+ */
+ public static void main(final String[] args) throws Exception {
+ final CommandLineArguments cla = BasicCommandLineArguments.parseCommandLine(args, TcpSocketServer.class, new CommandLineArguments());
+ if (cla.isHelp()) {
+ return;
+ }
+ if (cla.getConfigLocation() != null) {
+ ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(cla.getConfigLocation()));
+ }
+ final TcpSocketServer<ObjectInputStream> socketServer = TcpSocketServer.createSerializedSocketServer(
+ cla.getPort(), cla.getBacklog(), cla.getLocalBindAddress(), cla.getAllowedClasses());
+ final Thread serverThread = socketServer.startNewThread();
+ if (cla.isInteractive()) {
+ socketServer.awaitTermination(serverThread);
+ }
+ }
+
+ private final ConcurrentMap<Long, SocketHandler> handlers = new ConcurrentHashMap<>();
+
+ private final ServerSocket serverSocket;
+
+ /**
+ * Constructor.
+ *
+ * @param port
+ * The server socket port.
+ * @param backlog
+ * The server socket backlog.
+ * @param localBindAddress TODO
+ * @param logEventInput
+ * the log even input
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ * @since 2.7
+ */
+ @SuppressWarnings("resource")
+ public TcpSocketServer(final int port, final int backlog, final InetAddress localBindAddress, final LogEventBridge<T> logEventInput) throws IOException {
+ this(port, logEventInput, new ServerSocket(port, backlog, localBindAddress));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param port
+ * to listen.
+ * @param logEventInput
+ * the log even input
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ */
+ public TcpSocketServer(final int port, final LogEventBridge<T> logEventInput) throws IOException {
+ this(port, logEventInput, extracted(port));
+ }
+
+ private static ServerSocket extracted(final int port) throws IOException {
+ return new ServerSocket(port);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param port
+ * to listen.
+ * @param logEventInput
+ * the log even input
+ * @param serverSocket
+ * the socket server
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ */
+ public TcpSocketServer(final int port, final LogEventBridge<T> logEventInput, final ServerSocket serverSocket)
+ throws IOException {
+ super(port, logEventInput);
+ this.serverSocket = serverSocket;
+ }
+
+ /**
+ * Accept incoming events and processes them.
+ */
+ @Override
+ public void run() {
+ final EntryMessage entry = logger.traceEntry();
+ while (isActive()) {
+ if (serverSocket.isClosed()) {
+ return;
+ }
+ try {
+ // Accept incoming connections.
+ logger.debug("Listening for a connection {}...", serverSocket);
+ final Socket clientSocket = serverSocket.accept();
+ logger.debug("Acepted connection on {}...", serverSocket);
+ logger.debug("Socket accepted: {}", clientSocket);
+ clientSocket.setSoLinger(true, 0);
+
+ // accept() will block until a client connects to the server.
+ // If execution reaches this point, then it means that a client
+ // socket has been accepted.
+
+ final SocketHandler handler = new SocketHandler(clientSocket);
+ handlers.put(Long.valueOf(handler.getId()), handler);
+ handler.start();
+ } catch (final IOException e) {
+ if (serverSocket.isClosed()) {
+ // OK we're done.
+ logger.traceExit(entry);
+ return;
+ }
+ logger.error("Exception encountered on accept. Ignoring. Stack trace :", e);
+ }
+ }
+ for (final Map.Entry<Long, SocketHandler> handlerEntry : handlers.entrySet()) {
+ final SocketHandler handler = handlerEntry.getValue();
+ handler.shutdown();
+ try {
+ handler.join();
+ } catch (final InterruptedException ignored) {
+ // Ignore the exception
+ }
+ }
+ logger.traceExit(entry);
+ }
+
+ /**
+ * Shutdown the server.
+ *
+ * @throws IOException if the server socket could not be closed
+ */
+ @Override
+ public void shutdown() throws IOException {
+ final EntryMessage entry = logger.traceEntry();
+ setActive(false);
+ Thread.currentThread().interrupt();
+ serverSocket.close();
+ logger.traceExit(entry);
+ }
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/UdpSocketServer.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/UdpSocketServer.java
new file mode 100644
index 0000000..12a8450
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/UdpSocketServer.java
@@ -0,0 +1,185 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.OptionalDataException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.util.List;
+
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.util.BasicCommandLineArguments;
+
+/**
+ * Listens for Log4j events on a datagram socket and passes them on to Log4j.
+ *
+ * @param <T>
+ * The kind of input stream read
+ * @see #main(String[])
+ */
+public class UdpSocketServer<T extends InputStream> extends AbstractSocketServer<T> {
+
+ /**
+ * Creates a socket server that reads JSON log events.
+ *
+ * @param port
+ * the port to listen
+ * @return a new a socket server
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ */
+ public static UdpSocketServer<InputStream> createJsonSocketServer(final int port) throws IOException {
+ return new UdpSocketServer<>(port, new JsonInputStreamLogEventBridge());
+ }
+
+ /**
+ * Creates a socket server that reads serialized log events.
+ *
+ * @param port
+ * the port to listen
+ * @return a new a socket server
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ */
+ public static UdpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port) throws IOException {
+ return new UdpSocketServer<>(port, new ObjectInputStreamLogEventBridge());
+ }
+
+ /**
+ * Creates a socket server that reads serialized log events.
+ *
+ * @param port the port to listen
+ * @param allowedClasses additional classes to allow for deserialization
+ * @return a new a socket server
+ * @throws IOException if an I/O error occurs when opening the socket.
+ * @since 2.8.2
+ */
+ public static UdpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port,
+ final List<String> allowedClasses)
+ throws IOException {
+ return new UdpSocketServer<>(port, new ObjectInputStreamLogEventBridge(allowedClasses));
+ }
+
+ /**
+ * Creates a socket server that reads XML log events.
+ *
+ * @param port
+ * the port to listen
+ * @return a new a socket server
+ * @throws IOException
+ * if an I/O error occurs when opening the socket.
+ */
+ public static UdpSocketServer<InputStream> createXmlSocketServer(final int port) throws IOException {
+ return new UdpSocketServer<>(port, new XmlInputStreamLogEventBridge());
+ }
+
+ /**
+ * Main startup for the server. Run with "--help" for to print command line help on the console.
+ *
+ * @param args
+ * The command line arguments.
+ * @throws Exception
+ * if an error occurs.
+ */
+ public static void main(final String[] args) throws Exception {
+ final CommandLineArguments cla = BasicCommandLineArguments.parseCommandLine(args, UdpSocketServer.class, new CommandLineArguments());
+ if (cla.isHelp()) {
+ return;
+ }
+ if (cla.getConfigLocation() != null) {
+ ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(cla.getConfigLocation()));
+ }
+ final UdpSocketServer<ObjectInputStream> socketServer = UdpSocketServer
+ .createSerializedSocketServer(cla.getPort(), cla.getAllowedClasses());
+ final Thread serverThread = socketServer.startNewThread();
+ if (cla.isInteractive()) {
+ socketServer.awaitTermination(serverThread);
+ }
+ }
+
+ private final DatagramSocket datagramSocket;
+
+ // max size so we only have to deal with one packet
+ private final int maxBufferSize = 1024 * 65 + 1024;
+
+ /**
+ * Constructor.
+ *
+ * @param port
+ * to listen on.
+ * @param logEventInput
+ * @throws IOException
+ * If an error occurs.
+ */
+ public UdpSocketServer(final int port, final LogEventBridge<T> logEventInput) throws IOException {
+ super(port, logEventInput);
+ this.datagramSocket = new DatagramSocket(port);
+ }
+
+ /**
+ * Accept incoming events and processes them.
+ */
+ @Override
+ public void run() {
+ while (isActive()) {
+ if (datagramSocket.isClosed()) {
+ // OK we're done.
+ return;
+ }
+ try {
+ final byte[] buf = new byte[maxBufferSize];
+ final DatagramPacket packet = new DatagramPacket(buf, buf.length);
+ datagramSocket.receive(packet);
+ final ByteArrayInputStream bais = new ByteArrayInputStream(packet.getData(), packet.getOffset(), packet.getLength());
+ logEventInput.logEvents(logEventInput.wrapStream(bais), this);
+ } catch (final OptionalDataException e) {
+ if (datagramSocket.isClosed()) {
+ // OK we're done.
+ return;
+ }
+ logger.error("OptionalDataException eof=" + e.eof + " length=" + e.length, e);
+ } catch (final EOFException e) {
+ if (datagramSocket.isClosed()) {
+ // OK we're done.
+ return;
+ }
+ logger.info("EOF encountered");
+ } catch (final IOException e) {
+ if (datagramSocket.isClosed()) {
+ // OK we're done.
+ return;
+ }
+ logger.error("Exception encountered on accept. Ignoring. Stack Trace :", e);
+ }
+ }
+ }
+
+ /**
+ * Shutdown the server.
+ */
+ @Override
+ public void shutdown() {
+ this.setActive(false);
+ Thread.currentThread().interrupt();
+ datagramSocket.close();
+ }
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/XmlInputStreamLogEventBridge.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/XmlInputStreamLogEventBridge.java
new file mode 100644
index 0000000..683ae07
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/XmlInputStreamLogEventBridge.java
@@ -0,0 +1,54 @@
+/*
+ * 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.InputStream;
+import java.nio.charset.Charset;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.jackson.Log4jXmlObjectMapper;
+
+/**
+ * Reads and logs {@link LogEvent}s from an {@link InputStream}.
+ */
+public class XmlInputStreamLogEventBridge extends InputStreamLogEventBridge {
+
+ private static final String EVENT_END = "</Event>";
+ private static final String EVENT_START_NS_N = "<Event>";
+ private static final String EVENT_START_NS_Y = "<Event ";
+
+ public XmlInputStreamLogEventBridge() {
+ this(1024, Charset.defaultCharset());
+ }
+
+ public XmlInputStreamLogEventBridge(final int bufferSize, final Charset charset) {
+ super(new Log4jXmlObjectMapper(), bufferSize, charset, EVENT_END);
+ }
+
+ @Override
+ protected int[] getEventIndices(final String text, final int beginIndex) {
+ int start = text.indexOf(EVENT_START_NS_Y, beginIndex);
+ int startLen = EVENT_START_NS_Y.length();
+ if (start < 0) {
+ start = text.indexOf(EVENT_START_NS_N, beginIndex);
+ startLen = EVENT_START_NS_N.length();
+ }
+ final int end = start < 0 ? -1 : text.indexOf(EVENT_END, start + startLen);
+ return new int[] { start, end };
+ }
+
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/AbstractJmsReceiver.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/AbstractJmsReceiver.java
new file mode 100644
index 0000000..373d31c
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/AbstractJmsReceiver.java
@@ -0,0 +1,48 @@
+/*
+ * 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.mom.jms;
+
+import org.apache.logging.log4j.server.JmsServer;
+
+/**
+ * Common JMS server functionality.
+ *
+ * @since 2.6
+ */
+public abstract class AbstractJmsReceiver {
+
+ /**
+ * Prints out usage information to {@linkplain System#err standard error}.
+ */
+ protected abstract void usage();
+
+ /**
+ * Executes a JmsServer with the given command line arguments.
+ *
+ * @param args command line arguments
+ * @throws Exception
+ */
+ protected void doMain(final String... args) throws Exception {
+ if (args.length != 4) {
+ usage();
+ System.exit(1);
+ }
+ final JmsServer server = new JmsServer(args[0], args[1], args[2], args[3]);
+ server.run();
+ }
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsQueueReceiver.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsQueueReceiver.java
new file mode 100644
index 0000000..a7f75ec
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsQueueReceiver.java
@@ -0,0 +1,46 @@
+/*
+ * 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.mom.jms;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects that all messages will
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ private JmsQueueReceiver() {
+ }
+
+ /**
+ * Main startup for the receiver.
+ *
+ * @param args The command line arguments.
+ * @throws Exception if an error occurs.
+ */
+ public static void main(final String[] args) throws Exception {
+ final JmsQueueReceiver receiver = new JmsQueueReceiver();
+ receiver.doMain(args);
+ }
+
+ @Override
+ protected void usage() {
+ System.err.println("Wrong number of arguments.");
+ System.err.println("Usage: java " + JmsQueueReceiver.class.getName()
+ + " QueueConnectionFactoryBindingName QueueBindingName username password");
+ }
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsTopicReceiver.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsTopicReceiver.java
new file mode 100644
index 0000000..21828d7
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsTopicReceiver.java
@@ -0,0 +1,46 @@
+/*
+ * 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.mom.jms;
+
+/**
+ * Receives Topic messages that contain LogEvents. This implementation expects that all messages
+ * are serialized log events.
+ */
+public class JmsTopicReceiver extends AbstractJmsReceiver {
+
+ private JmsTopicReceiver() {
+ }
+
+ /**
+ * Main startup for the receiver.
+ *
+ * @param args The command line arguments.
+ * @throws Exception if an error occurs.
+ */
+ public static void main(final String[] args) throws Exception {
+ final JmsTopicReceiver receiver = new JmsTopicReceiver();
+ receiver.doMain(args);
+ }
+
+ @Override
+ protected void usage() {
+ System.err.println("Wrong number of arguments.");
+ System.err.println("Usage: java " + JmsTopicReceiver.class.getName()
+ + " TopicConnectionFactoryBindingName TopicBindingName username password");
+ }
+}
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/package-info.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/package-info.java
new file mode 100644
index 0000000..dd9c90e
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Supporting network code for JMS appenders.
+ *
+ * <p>Note that you can use JmsQueueReceiver or JmsTopicReceiver as executable main classes to receive log events over
+ * JMS (sent via the appropriate JMS appender) that can be subsequently logged according to the configuration given to
+ * the running process. Of course, use of these classes as standalone executables are entirely optional and can
+ * be used directly in your application (e.g., through your Spring {@code beans.xml} configuration).</p>
+ */
+package org.apache.logging.log4j.server.mom.jms;
diff --git a/log4j-server/src/main/java/org/apache/logging/log4j/server/package-info.java b/log4j-server/src/main/java/org/apache/logging/log4j/server/package-info.java
new file mode 100644
index 0000000..c446a9d
--- /dev/null
+++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+/**
+ * Standalone server classes for consuming log events over a network. Each of the various servers should be used with
+ * another Log4j configuration to handle incoming {@link org.apache.logging.log4j.core.LogEvent}s. It is recommended
+ * to consider using the <a href="../../../../../../../../../manual/appenders.html#FlumeAppender">Flume Appender</a>
+ * for highly reliable networked logging.
+ */
+package org.apache.logging.log4j.server;
diff --git a/log4j-server/src/site/markdown/index.md b/log4j-server/src/site/markdown/index.md
new file mode 100644
index 0000000..b3d96ba
--- /dev/null
+++ b/log4j-server/src/site/markdown/index.md
@@ -0,0 +1,30 @@
+<!-- vim: set syn=markdown : -->
+<!--
+ 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.
+-->
+
+# Log4j Server components
+
+## Log4j Server components
+
+Standalone server classes for consuming log events over a network. Each of the various servers should be used with
+another Log4j configuration to handle incoming log events. It is recommended to consider using the
+[Flume Appender](../manual/appenders.html#FlumeAppender) for highly reliable networked logging.
+
+## Requirements
+
+The Log4j Server components requires the Log4j 2 API and core. This component was introduced in Log4j 2.8.2,
+before it was part of log4j-core. For more information, see [Runtime Dependencies](../manual/runtime-dependencies.html).
diff --git a/log4j-server/src/site/site.xml b/log4j-server/src/site/site.xml
new file mode 100644
index 0000000..f863dbf
--- /dev/null
+++ b/log4j-server/src/site/site.xml
@@ -0,0 +1,52 @@
+<!--
+ 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.
+
+-->
+<project name="Log4j IOStreams"
+ xmlns="http://maven.apache.org/DECORATION/1.4.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/DECORATION/1.4.0 http://maven.apache.org/xsd/decoration-1.4.0.xsd">
+ <body>
+ <links>
+ <item name="Apache" href="http://www.apache.org/" />
+ <item name="Logging Services" href="http://logging.apache.org/"/>
+ <item name="Log4j" href="../index.html"/>
+ </links>
+
+ <!-- Component-specific reports -->
+ <menu ref="reports"/>
+
+ <!-- Overall Project Info -->
+ <menu name="Log4j Project Information" img="icon-info-sign">
+ <item name="Dependencies" href="../dependencies.html" />
+ <item name="Dependency Convergence" href="../dependency-convergence.html" />
+ <item name="Dependency Management" href="../dependency-management.html" />
+ <item name="Project Team" href="../team-list.html" />
+ <item name="Mailing Lists" href="../mail-lists.html" />
+ <item name="Issue Tracking" href="../issue-tracking.html" />
+ <item name="Project License" href="../license.html" />
+ <item name="Source Repository" href="../source-repository.html" />
+ <item name="Project Summary" href="../project-summary.html" />
+ </menu>
+
+ <menu name="Log4j Project Reports" img="icon-cog">
+ <item name="Changes Report" href="../changes-report.html" />
+ <item name="JIRA Report" href="../jira-report.html" />
+ <item name="Surefire Report" href="../surefire-report.html" />
+ <item name="RAT Report" href="../rat-report.html" />
+ </menu>
+ </body>
+</project>
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/AbstractSocketServerTest.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/AbstractSocketServerTest.java
new file mode 100644
index 0000000..dfc44ea
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/AbstractSocketServerTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.IOException;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.SocketAppender;
+import org.apache.logging.log4j.core.layout.JsonLayout;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.layout.XmlLayout;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.test.AvailablePortFinder;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.After;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public abstract class AbstractSocketServerTest {
+
+ protected static Thread thread;
+
+ private static final String MESSAGE = "This is test message";
+
+ private static final String MESSAGE_2 = "This is test message 2";
+
+ private static final String MESSAGE_WITH_SPECIAL_CHARS = "{This}\n[is]\"n\"a\"\r\ntrue:\n\ttest,\nmessage";
+
+ static final int PORT_NUM = AvailablePortFinder.getNextAvailable();
+
+ static final int PORT = PORT_NUM;
+
+ private final LoggerContext ctx = LoggerContext.getContext(false);
+
+ private final boolean expectLengthException;
+
+ protected final int port;
+
+ protected final Protocol protocol;
+
+ private final Logger rootLogger = ctx.getLogger(AbstractSocketServerTest.class.getSimpleName());
+
+ protected AbstractSocketServerTest(final Protocol protocol, final int port, final boolean expectLengthException) {
+ this.protocol = protocol;
+ this.port = port;
+ this.expectLengthException = expectLengthException;
+ }
+
+ protected Layout<String> createJsonLayout() {
+ // @formatter: off
+ return JsonLayout.newBuilder()
+ .setLocationInfo(true)
+ .setProperties(true)
+ .setPropertiesAsList(false)
+ .setComplete(false)
+ .setCompact(false)
+ .setEventEol(false)
+ .setIncludeStacktrace(true)
+ .build();
+ // @formatter: on
+
+ //return JsonLayout.createLayout(null, true, true, false, false, false, false, null, null, null, true);
+ }
+
+ protected abstract Layout<? extends Serializable> createLayout();
+
+ protected Layout<? extends Serializable> createSerializedLayout() {
+ return null;
+ }
+
+ protected Layout<String> createXmlLayout() {
+ return XmlLayout.createLayout(true, true, false, false, null, true);
+ }
+
+ @After
+ public void tearDown() {
+ final Map<String, Appender> map = rootLogger.getAppenders();
+ for (final Map.Entry<String, Appender> entry : map.entrySet()) {
+ final Appender appender = entry.getValue();
+ rootLogger.removeAppender(appender);
+ appender.stop();
+ }
+ }
+
+ @Test
+ @Ignore("Broken test?")
+ public void test1000ShortMessages() throws Exception {
+ testServer(1000);
+ }
+
+ @Test
+ @Ignore("Broken test?")
+ public void test100ShortMessages() throws Exception {
+ testServer(100);
+ }
+
+ @Test
+ public void test10ShortMessages() throws Exception {
+ testServer(10);
+ }
+
+ @Test
+ public void test1ShortMessages() throws Exception {
+ testServer(1);
+ }
+
+ @Test
+ public void test2ShortMessages() throws Exception {
+ testServer(2);
+ }
+
+ @Test
+ public void test64KBMessages() throws Exception {
+ final char[] a64K = new char[1024 * 64];
+ Arrays.fill(a64K, 'a');
+ final String m1 = new String(a64K);
+ final String m2 = MESSAGE_2 + m1;
+ if (expectLengthException) {
+ try {
+ testServer(m1, m2);
+ } catch (final AppenderLoggingException are) {
+ assertTrue("", are.getCause() != null && are.getCause() instanceof IOException);
+ // Failure expected.
+ }
+ } else {
+ testServer(m1, m2);
+ }
+ }
+
+
+ @Test
+ public void testMessagesWithSpecialChars() throws Exception {
+ testServer(MESSAGE_WITH_SPECIAL_CHARS);
+ }
+
+
+ private void testServer(final int size) throws Exception {
+ final String[] messages = new String[size];
+ for (int i = 0; i < messages.length; i++) {
+ messages[i] = MESSAGE + " " + i;
+ }
+ testServer(messages);
+ }
+
+ protected void testServer(final String... messages) throws Exception {
+ final Filter socketFilter = new ThreadNameFilter(Filter.Result.NEUTRAL, Filter.Result.DENY);
+ final Filter serverFilter = new ThreadNameFilter(Filter.Result.DENY, Filter.Result.NEUTRAL);
+ final Layout<? extends Serializable> socketLayout = createLayout();
+ final SocketAppender socketAppender = createSocketAppender(socketFilter, socketLayout);
+ socketAppender.start();
+ final ListAppender listAppender = new ListAppender("Events", serverFilter, null, false, false);
+ listAppender.start();
+ final PatternLayout layout = PatternLayout.newBuilder().withPattern("%m %ex%n").build();
+ final ConsoleAppender console = ConsoleAppender.createDefaultAppenderForLayout(layout);
+ final Logger serverLogger = ctx.getLogger(this.getClass().getName());
+ serverLogger.addAppender(console);
+ serverLogger.setAdditive(false);
+
+ // set appender on root and set level to debug
+ rootLogger.addAppender(socketAppender);
+ rootLogger.addAppender(listAppender);
+ rootLogger.setAdditive(false);
+ rootLogger.setLevel(Level.DEBUG);
+ for (final String message : messages) {
+ rootLogger.debug(message);
+ }
+ final int MAX_TRIES = 400;
+ for (int i = 0; i < MAX_TRIES; i++) {
+ if (listAppender.getEvents().size() < messages.length) {
+ try {
+ // Let the server-side read the messages.
+ Thread.sleep(50);
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ break;
+ }
+ }
+ final List<LogEvent> events = listAppender.getEvents();
+ assertNotNull("No event retrieved", events);
+ assertEquals("Incorrect number of events received", messages.length, events.size());
+ for (int i = 0; i < messages.length; i++) {
+ assertTrue("Incorrect event", events.get(i).getMessage().getFormattedMessage().equals(messages[i]));
+ }
+ }
+
+ protected SocketAppender createSocketAppender(final Filter socketFilter,
+ final Layout<? extends Serializable> socketLayout) {
+ // @formatter:off
+ return SocketAppender.newBuilder()
+ .withProtocol(this.protocol)
+ .withHost("localhost")
+ .withPort(this.port)
+ .withReconnectDelayMillis(-1)
+ .withName("test")
+ .withImmediateFlush(true)
+ .withImmediateFail(false)
+ .withIgnoreExceptions(false)
+ .withLayout(socketLayout)
+ .withFilter(socketFilter)
+ .build();
+ // @formatter:on
+ }
+
+}
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/SslXmlSocketServerTest.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/SslXmlSocketServerTest.java
new file mode 100644
index 0000000..1a51244
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/SslXmlSocketServerTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.nio.charset.Charset;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.SocketAppender;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.core.net.ssl.KeyStoreConfiguration;
+import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
+import org.apache.logging.log4j.core.net.ssl.StoreConfigurationException;
+import org.apache.logging.log4j.core.net.ssl.TestConstants;
+import org.apache.logging.log4j.core.net.ssl.TrustStoreConfiguration;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class SslXmlSocketServerTest extends AbstractSocketServerTest {
+
+ private static TcpSocketServer<InputStream> server;
+
+ private static SslConfiguration sslConfiguration;
+
+ private static void initServerSocketFactory() throws StoreConfigurationException {
+ final KeyStoreConfiguration ksc = new KeyStoreConfiguration(TestConstants.KEYSTORE_FILE,
+ TestConstants.KEYSTORE_PWD, TestConstants.KEYSTORE_TYPE, null);
+ final TrustStoreConfiguration tsc = new TrustStoreConfiguration(TestConstants.TRUSTSTORE_FILE,
+ TestConstants.TRUSTSTORE_PWD, null, null);
+ sslConfiguration = SslConfiguration.createSSLConfiguration(null, ksc, tsc);
+ }
+
+ @Override
+ protected SocketAppender createSocketAppender(final Filter socketFilter,
+ final Layout<? extends Serializable> socketLayout) {
+ // @formatter:off
+ return SocketAppender.newBuilder()
+ .withProtocol(this.protocol)
+ .withHost("localhost")
+ .withPort(this.port)
+ .withReconnectDelayMillis(-1)
+ .withName("test")
+ .withImmediateFlush(true)
+ .withImmediateFail(false)
+ .withIgnoreExceptions(false)
+ .withLayout(socketLayout)
+ .withFilter(socketFilter)
+ .withSslConfiguration(sslConfiguration)
+ .build();
+ // @formatter:on
+ }
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ (LoggerContext.getContext(false)).reconfigure();
+ initServerSocketFactory();
+ // Use a large buffer just to test the code, the UDP test uses a tiny buffer
+ server = new SecureTcpSocketServer<>(PORT_NUM, new XmlInputStreamLogEventBridge(1024 * 100,
+ Charset.defaultCharset()), sslConfiguration);
+ thread = server.startNewThread();
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ try {
+ server.shutdown();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ try {
+ thread.join();
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ }
+
+ public SslXmlSocketServerTest() {
+ super(Protocol.SSL, PORT, false);
+ }
+
+ @Override
+ protected Layout<String> createLayout() {
+ return super.createXmlLayout();
+ }
+
+}
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/TcpJsonSocketServerTest.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/TcpJsonSocketServerTest.java
new file mode 100644
index 0000000..f12c908
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/TcpJsonSocketServerTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class TcpJsonSocketServerTest extends AbstractSocketServerTest {
+
+ private static TcpSocketServer<InputStream> server;
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ (LoggerContext.getContext(false)).reconfigure();
+ server = TcpSocketServer.createJsonSocketServer(PORT_NUM);
+ thread = server.startNewThread();
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ try {
+ server.shutdown();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ try {
+ thread.join();
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ }
+
+ public TcpJsonSocketServerTest() {
+ super(Protocol.TCP, PORT, false);
+ }
+
+ @Override
+ protected Layout<String> createLayout() {
+ return super.createJsonLayout();
+ }
+
+}
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/TcpSerializedSocketServerTest.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/TcpSerializedSocketServerTest.java
new file mode 100644
index 0000000..275af20
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/TcpSerializedSocketServerTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class TcpSerializedSocketServerTest extends AbstractSocketServerTest {
+
+ private static TcpSocketServer<ObjectInputStream> server;
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ (LoggerContext.getContext(false)).reconfigure();
+ server = TcpSocketServer.createSerializedSocketServer(PORT_NUM);
+ thread = server.startNewThread();
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ try {
+ server.shutdown();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ try {
+ thread.join();
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ }
+
+ public TcpSerializedSocketServerTest() {
+ super(Protocol.TCP, PORT, false);
+ }
+
+ @Override
+ protected Layout<? extends Serializable> createLayout() {
+ return super.createSerializedLayout();
+ }
+
+}
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/TcpXmlSocketServerTest.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/TcpXmlSocketServerTest.java
new file mode 100644
index 0000000..2b2cfd7
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/TcpXmlSocketServerTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class TcpXmlSocketServerTest extends AbstractSocketServerTest {
+
+ private static TcpSocketServer<InputStream> server;
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ (LoggerContext.getContext(false)).reconfigure();
+ // Use a large buffer just to test the code, the UDP test uses a tiny buffer
+ server = new TcpSocketServer<>(PORT_NUM, new XmlInputStreamLogEventBridge(1024 * 100,
+ Charset.defaultCharset()));
+ thread = server.startNewThread();
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ try {
+ server.shutdown();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ try {
+ thread.join();
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ }
+
+ public TcpXmlSocketServerTest() {
+ super(Protocol.TCP, PORT, false);
+ }
+
+ @Override
+ protected Layout<String> createLayout() {
+ return super.createXmlLayout();
+ }
+
+}
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/ThreadIdFilter.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/ThreadIdFilter.java
new file mode 100644
index 0000000..d98e3f4
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/ThreadIdFilter.java
@@ -0,0 +1,40 @@
+/*
+ * 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 org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.filter.AbstractFilter;
+
+/**
+ * TODO Should use thread ID cache?
+ * @since 2.6
+ */
+public class ThreadIdFilter extends AbstractFilter {
+
+ private static final long serialVersionUID = 1L;
+
+ public ThreadIdFilter(final Result onMatch, final Result onMismatch) {
+ super(onMatch, onMismatch);
+ }
+
+ @Override
+ public Filter.Result filter(final LogEvent event) {
+ return event.getThreadId() == Thread.currentThread().getId() ? onMatch : onMismatch;
+ }
+}
\ No newline at end of file
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/ThreadNameFilter.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/ThreadNameFilter.java
new file mode 100644
index 0000000..4204ac1
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/ThreadNameFilter.java
@@ -0,0 +1,39 @@
+/*
+ * 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 org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.filter.AbstractFilter;
+
+/**
+ * TODO Should use thread name cache?
+ */
+public class ThreadNameFilter extends AbstractFilter {
+
+ private static final long serialVersionUID = 1L;
+
+ public ThreadNameFilter(final Result onMatch, final Result onMismatch) {
+ super(onMatch, onMismatch);
+ }
+
+ @Override
+ public Filter.Result filter(final LogEvent event) {
+ return event.getThreadName().equals(Thread.currentThread().getName()) ? onMatch : onMismatch;
+ }
+}
\ No newline at end of file
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/ThreadPriorityFilter.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/ThreadPriorityFilter.java
new file mode 100644
index 0000000..6074f86
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/ThreadPriorityFilter.java
@@ -0,0 +1,40 @@
+/*
+ * 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 org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.filter.AbstractFilter;
+
+/**
+ * TODO Should use thread priority cache?
+ * @since 2.6
+ */
+public class ThreadPriorityFilter extends AbstractFilter {
+
+ private static final long serialVersionUID = 1L;
+
+ public ThreadPriorityFilter(final Result onMatch, final Result onMismatch) {
+ super(onMatch, onMismatch);
+ }
+
+ @Override
+ public Filter.Result filter(final LogEvent event) {
+ return event.getThreadPriority() == Thread.currentThread().getPriority() ? onMatch : onMismatch;
+ }
+}
\ No newline at end of file
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/UdpJsonSocketServerTest.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/UdpJsonSocketServerTest.java
new file mode 100644
index 0000000..7bd36f9
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/UdpJsonSocketServerTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.InputStream;
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class UdpJsonSocketServerTest extends AbstractSocketServerTest {
+
+ private static UdpSocketServer<InputStream> server;
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ (LoggerContext.getContext(false)).reconfigure();
+ server = UdpSocketServer.createJsonSocketServer(PORT_NUM);
+ thread = server.startNewThread();
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ server.shutdown();
+ try {
+ thread.join();
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ }
+
+ public UdpJsonSocketServerTest() {
+ super(Protocol.UDP, PORT, true);
+ }
+
+ @Override
+ protected Layout<? extends Serializable> createLayout() {
+ return super.createJsonLayout();
+ }
+
+}
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/UdpSerializedSocketServerTest.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/UdpSerializedSocketServerTest.java
new file mode 100644
index 0000000..9c806af
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/UdpSerializedSocketServerTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.ObjectInputStream;
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+
+@Ignore
+public class UdpSerializedSocketServerTest extends AbstractSocketServerTest {
+
+ private static UdpSocketServer<ObjectInputStream> server;
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ (LoggerContext.getContext(false)).reconfigure();
+ server = UdpSocketServer.createSerializedSocketServer(PORT_NUM);
+ thread = server.startNewThread();
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ server.shutdown();
+ try {
+ thread.join();
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ }
+
+ public UdpSerializedSocketServerTest() {
+ super(Protocol.UDP, PORT, true);
+ }
+
+ @Override
+ protected Layout<? extends Serializable> createLayout() {
+ return super.createSerializedLayout();
+ }
+
+}
diff --git a/log4j-server/src/test/java/org/apache/logging/log4j/server/UdpXmlSocketServerTest.java b/log4j-server/src/test/java/org/apache/logging/log4j/server/UdpXmlSocketServerTest.java
new file mode 100644
index 0000000..0299312
--- /dev/null
+++ b/log4j-server/src/test/java/org/apache/logging/log4j/server/UdpXmlSocketServerTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.InputStream;
+import java.io.Serializable;
+import java.nio.charset.Charset;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class UdpXmlSocketServerTest extends AbstractSocketServerTest {
+
+ private static UdpSocketServer<InputStream> server;
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ (LoggerContext.getContext(false)).reconfigure();
+ // Use a tiny buffer just to test the code, the TCP test uses a large buffer
+ server = new UdpSocketServer<>(PORT_NUM, new XmlInputStreamLogEventBridge(100,
+ Charset.defaultCharset()));
+ thread = server.startNewThread();
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ server.shutdown();
+ try {
+ thread.join();
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ }
+
+ public UdpXmlSocketServerTest() {
+ super(Protocol.UDP, PORT, true);
+ }
+
+ @Override
+ protected Layout<? extends Serializable> createLayout() {
+ return super.createXmlLayout();
+ }
+
+}
diff --git a/log4j-server/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.jks b/log4j-server/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.jks
new file mode 100644
index 0000000..36f11b6
--- /dev/null
+++ b/log4j-server/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.jks
Binary files differ
diff --git a/log4j-server/src/test/resources/org/apache/logging/log4j/core/net/ssl/truststore.jks b/log4j-server/src/test/resources/org/apache/logging/log4j/core/net/ssl/truststore.jks
new file mode 100644
index 0000000..0e6aaf2
--- /dev/null
+++ b/log4j-server/src/test/resources/org/apache/logging/log4j/core/net/ssl/truststore.jks
Binary files differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..4e07674
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,991 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-tools</artifactId>
+ <packaging>pom</packaging>
+ <name>Apache Log4j 2 Tools</name>
+ <version>2.8.3-SNAPSHOT</version>
+ <parent>
+ <groupId>org.apache.logging</groupId>
+ <artifactId>logging-parent</artifactId>
+ <version>1</version>
+ </parent>
+ <description>Apache Log4j 2 Tools</description>
+ <url>https://logging.apache.org/log4j/2.x/</url>
+ <issueManagement>
+ <system>JIRA</system>
+ <url>https://issues.apache.org/jira/browse/LOG4J2</url>
+ </issueManagement>
+ <inceptionYear>1999</inceptionYear>
+ <developers>
+ <developer>
+ <id>rgoers</id>
+ <name>Ralph Goers</name>
+ <email>rgoers@apache.org</email>
+ <organization>Nextiva</organization>
+ <roles>
+ <role>PMC Chair</role>
+ </roles>
+ <timezone>America/Phoenix</timezone>
+ </developer>
+ <developer>
+ <id>ggregory</id>
+ <name>Gary Gregory</name>
+ <email>ggregory@apache.org</email>
+ <organization>Rocket Software</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ <timezone>America/Los_Angeles</timezone>
+ </developer>
+ <developer>
+ <id>sdeboy</id>
+ <name>Scott Deboy</name>
+ <email>sdeboy@apache.org</email>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ <timezone>America/Los_Angeles</timezone>
+ </developer>
+ <developer>
+ <id>rpopma</id>
+ <name>Remko Popma</name>
+ <email>rpopma@apache.org</email>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ <timezone>Asia/Tokyo</timezone>
+ <properties>
+ <picUrl>http://people.apache.org/~rpopma/img/profilepic.jpg</picUrl>
+ </properties>
+ </developer>
+ <developer>
+ <id>nickwilliams</id>
+ <name>Nick Williams</name>
+ <email>nickwilliams@apache.org</email>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ <timezone>America/Chicago</timezone>
+ </developer>
+ <developer>
+ <id>mattsicker</id>
+ <name>Matt Sicker</name>
+ <email>mattsicker@apache.org</email>
+ <organization>SPR Consulting</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ <timezone>America/Chicago</timezone>
+ </developer>
+ <developer>
+ <id>bbrouwer</id>
+ <name>Bruce Brouwer</name>
+ <email>bruce.brouwer@gmail.com</email>
+ <roles>
+ <role>Committer</role>
+ </roles>
+ <timezone>America/Detroit</timezone>
+ </developer>
+ <developer>
+ <id>mikes</id>
+ <name>Mikael Ståldal</name>
+ <email>mikes@apache.org</email>
+ <organization>Magine TV</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ <timezone>Europe/Stockholm</timezone>
+ </developer>
+ </developers>
+ <!-- Contributors -->
+ <contributors>
+ <contributor>
+ <name>Murad Ersoy</name>
+ <email>muradersoy@gmail.com</email>
+ <url>https://www.behance.net/muradersoy</url>
+ <roles>
+ <role>Illustrator and Designer</role>
+ <role>created the new Log4j 2 logo.</role>
+ </roles>
+ <timezone>Europe/Istanbul</timezone>
+ <properties>
+ <picUrl>https://mir-s3-cdn-cf.behance.net/user/138/403dcf1521581.54d67f8fb01f7.jpg</picUrl>
+ </properties>
+ </contributor>
+ </contributors>
+ <mailingLists>
+ <mailingList>
+ <name>log4j-user</name>
+ <subscribe>log4j-user-subscribe@logging.apache.org</subscribe>
+ <unsubscribe>log4j-user-unsubscribe@logging.apache.org</unsubscribe>
+ <post>log4j-user@logging.apache.org</post>
+ <archive>https://lists.apache.org/list.html?log4j-user@logging.apache.org</archive>
+ <otherArchives>
+ <otherArchive>http://mail-archives.apache.org/mod_mbox/logging-log4j-user/</otherArchive>
+ <otherArchive>http://marc.info/?l=log4j-user</otherArchive>
+ <otherArchive>http://dir.gmane.org/gmane.comp.jakarta.log4j.user</otherArchive>
+ </otherArchives>
+ </mailingList>
+ <mailingList>
+ <name>log4j-dev</name>
+ <subscribe>log4j-dev-subscribe@logging.apache.org</subscribe>
+ <unsubscribe>log4j-dev-unsubscribe@logging.apache.org</unsubscribe>
+ <post>log4j-dev@logging.apache.org</post>
+ <archive>https://lists.apache.org/list.html?log4j-dev@logging.apache.org</archive>
+ <otherArchives>
+ <otherArchive>http://mail-archives.apache.org/mod_mbox/logging-log4j-dev/</otherArchive>
+ <otherArchive>http://marc.info/?l=log4j-dev</otherArchive>
+ <otherArchive>http://dir.gmane.org/gmane.comp.jakarta.log4j.devel</otherArchive>
+ </otherArchives>
+ </mailingList>
+ </mailingLists>
+ <scm>
+ <connection>scm:git:https://git-wip-us.apache.org/repos/asf/logging-log4j-tools.git</connection>
+ <developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/logging-log4j-tools.git</developerConnection>
+ <url>https://git-wip-us.apache.org/repos/asf?p=logging-log4j-tools.git;a=summary</url>
+ <tag>log4j-${Log4jReleaseVersion}</tag>
+ </scm>
+ <properties>
+ <!-- make sure to update these for each release! -->
+ <log4jParentDir>${basedir}</log4jParentDir>
+ <Log4jReleaseVersion>2.8.2</Log4jReleaseVersion>
+ <!--<Log4jReleaseManager>Ralph Goers</Log4jReleaseManager>-->
+ <!--<Log4jReleaseKey>B3D8E1BA</Log4jReleaseKey>-->
+ <Log4jReleaseManager>Matt Sicker</Log4jReleaseManager>
+ <Log4jReleaseKey>FA1C814D</Log4jReleaseKey>
+ <!-- note that any properties you want available in velocity templates must not use periods! -->
+ <jackson2Version>2.8.7</jackson2Version>
+ <compiler.plugin.version>3.6.0</compiler.plugin.version>
+ <pmd.plugin.version>3.7</pmd.plugin.version>
+ <findbugs.plugin.version>3.0.4</findbugs.plugin.version>
+ <changes.plugin.version>2.12.1</changes.plugin.version>
+ <javadoc.plugin.version>2.10.4</javadoc.plugin.version>
+ <!-- surefire.plugin.version 2.18 yields http://jira.codehaus.org/browse/SUREFIRE-1121, which is fixed in 2.18.1 -->
+ <!-- surefire.plugin.version 2.19 yields https://issues.apache.org/jira/browse/SUREFIRE-1193. -->
+ <surefire.plugin.version>2.19.1</surefire.plugin.version>
+ <failsafe.plugin.version>2.19.1</failsafe.plugin.version>
+ <checkstyle.plugin.version>2.17</checkstyle.plugin.version>
+ <rat.plugin.version>0.12</rat.plugin.version>
+ <pdf.plugin.version>1.2</pdf.plugin.version>
+ <cobertura.plugin.version>2.7</cobertura.plugin.version>
+ <jacoco.plugin.version>0.7.7.201606060606</jacoco.plugin.version>
+ <release.plugin.version>2.5.3</release.plugin.version>
+ <scm.plugin.version>1.9.5</scm.plugin.version>
+ <jxr.plugin.version>2.5</jxr.plugin.version>
+ <clirr.plugin.version>2.8</clirr.plugin.version>
+ <site.plugin.version>3.4</site.plugin.version>
+ <remote.resources.plugin.version>1.5</remote.resources.plugin.version>
+ <manifestfile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestfile>
+ <maven.compiler.source>1.7</maven.compiler.source>
+ <maven.compiler.target>1.7</maven.compiler.target>
+ <docLabel>Site Documentation</docLabel>
+ <projectDir />
+ <commonsLoggingVersion>1.2</commonsLoggingVersion>
+ <osgi.api.version>4.3.1</osgi.api.version>
+ <activemq.version>5.14.4</activemq.version>
+ <!-- Allow Clirr severity to be overriden by the command-line option -DminSeverity=level -->
+ <minSeverity>info</minSeverity>
+ <jctoolsVersion>1.2.1</jctoolsVersion>
+ <mockitoVersion>2.2.7</mockitoVersion>
+ <argLine>-Xms256m -Xmx1024m</argLine>
+ </properties>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>apache</id>
+ <url>https://repository.apache.org/content/repositories/releases/</url>
+ </pluginRepository>
+ </pluginRepositories>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>com.beust</groupId>
+ <artifactId>jcommander</artifactId>
+ <version>1.48</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>${jackson2Version}</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-xml</artifactId>
+ <version>${jackson2Version}</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ <version>1.0.1.Final</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <version>1.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>${mockitoVersion}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <!-- Using version 2.5.4 causes Jenkins to get compiler errors in log4j-perf. -->
+ <version>3.2.0</version>
+ <inherited>true</inherited>
+ <extensions>true</extensions>
+ <executions>
+ <execution>
+ <goals>
+ <goal>manifest</goal>
+ </goals>
+ <phase>process-classes</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-changes-plugin</artifactId>
+ <version>${changes.plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>${release.plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-scm-plugin</artifactId>
+ <version>${scm.plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>${checkstyle.plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>${javadoc.plugin.version}</version>
+ <configuration>
+ <bottom><![CDATA[<p align="center">Copyright © {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br />
+ Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo,
+ and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>]]></bottom>
+ <additionalparam>${javadoc.opts}</additionalparam>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <version>${pmd.plugin.version}</version>
+ </plugin>
+ <!-- some nice default compiler options -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>${compiler.plugin.version}</version>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ <showDeprecation>true</showDeprecation>
+ <showWarnings>true</showWarnings>
+ <encoding>UTF-8</encoding>
+ <fork>true</fork>
+ <meminitial>256</meminitial>
+ <maxmem>1024</maxmem>
+ <compilerArguments>
+ <Xmaxwarns>10000</Xmaxwarns>
+ <Xlint />
+ </compilerArguments>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>${surefire.plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>${failsafe.plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>3.0.1</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jxr-plugin</artifactId>
+ <version>${jxr.plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>clirr-maven-plugin</artifactId>
+ <version>${clirr.plugin.version}</version>
+ <configuration>
+ <minSeverity>${minSeverity}</minSeverity>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.eluder.coveralls</groupId>
+ <artifactId>coveralls-maven-plugin</artifactId>
+ <version>4.3.0</version>
+ </plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>${jacoco.plugin.version}</version>
+ <executions>
+ <execution>
+ <id>prepare-agent</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>default-report</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestFile>${manifestfile}</manifestFile>
+ <manifestEntries>
+ <Specification-Title>${project.name}</Specification-Title>
+ <Specification-Version>${project.version}</Specification-Version>
+ <Specification-Vendor>${project.organization.name}</Specification-Vendor>
+ <Implementation-Title>${project.name}</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+ <Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
+ <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
+ <X-Compile-Source-JDK>${maven.compiler.source}</X-Compile-Source-JDK>
+ <X-Compile-Target-JDK>${maven.compiler.target}</X-Compile-Target-JDK>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>3.0.1</version>
+ <executions>
+ <execution>
+ <id>copy-sitecss</id>
+ <!-- fetch site.xml before creating site documentation -->
+ <phase>pre-site</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/site</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${log4jParentDir}/src/site/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>${surefire.plugin.version}</version>
+ <configuration>
+ <systemPropertyVariables>
+ <java.awt.headless>true</java.awt.headless>
+ </systemPropertyVariables>
+ <forkCount>2C</forkCount>
+ <reuseForks>true</reuseForks>
+ <excludes>
+ <exclude>${log4j.skip.test1}</exclude>
+ <exclude>${log4j.skip.test2}</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>${failsafe.plugin.version}</version>
+ <executions>
+ <execution>
+ <id>integration-tests</id>
+ <goals>
+ <goal>integration-test</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>verify</id>
+ <goals>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <systemPropertyVariables>
+ <java.awt.headless>true</java.awt.headless>
+ </systemPropertyVariables>
+ <argLine>-Xms256m -Xmx1024m</argLine>
+ <forkCount>1</forkCount>
+ <reuseForks>false</reuseForks>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>${site.plugin.version}</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven.wagon</groupId>
+ <artifactId>wagon-ssh</artifactId>
+ <version>2.10</version>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <!-- only build English site even on other language OS -->
+ <locales>en</locales>
+ <!-- Exclude the navigation file for Maven 1 sites
+ and the changes file used by the changes-plugin,
+ as they interfere with the site generation. -->
+ <moduleExcludes>
+ <xdoc>navigation.xml,changes.xml</xdoc>
+ </moduleExcludes>
+ <templateDirectory>${log4jParentDir}/src/site</templateDirectory>
+ <template>site.vm</template>
+ </configuration>
+ </plugin>
+ <!-- <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>cobertura-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>clean</id>
+ <goals>
+ <goal>clean</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin> -->
+ <!-- We need to disable the standard ASF configuration to be able to publish our own notice and license files -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-remote-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>process</goal>
+ </goals>
+ <configuration>
+ <skip>true</skip>
+ <resourceBundles />
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pdf-plugin</artifactId>
+ <version>${pdf.plugin.version}</version>
+ <executions>
+ <execution>
+ <id>pdf</id>
+ <phase>site</phase>
+ <goals>
+ <goal>pdf</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.reporting.outputDirectory}</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ </plugin>
+ <!-- RAT report -->
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <version>${rat.plugin.version}</version>
+ <configuration>
+ <excludes>
+ <!-- Matches other RAT configurations in this POM -->
+ <exclude>src/main/resources/META-INF/services/**/*</exclude>
+ <!-- IntelliJ files -->
+ <exclude>.idea/**/*</exclude>
+ <exclude>src/test/resources/**/*</exclude>
+ <!-- IDE settings imports -->
+ <exclude>src/ide/**</exclude>
+ <!-- does it even make sense to apply a license to a GPG signature? -->
+ <exclude>**/*.asc</exclude>
+ <!-- jQuery is MIT-licensed, but RAT can't figure it out -->
+ <exclude>src/site/resources/js/jquery.js</exclude>
+ <exclude>src/site/resources/js/jquery.min.js</exclude>
+ <!-- Generated files -->
+ <exclude>log4j-distribution/target/**/*</exclude>
+ <exclude>log4j-distribution/.project</exclude>
+ <exclude>log4j-distribution/.settings/**</exclude>
+ <exclude>velocity.log</exclude>
+ <!-- Other -->
+ <exclude>felix-cache/**</exclude>
+ <exclude>RELEASE-NOTES.md</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <!-- DOAP (RDF) metadata generation -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-doap-plugin</artifactId>
+ <version>1.2</version>
+ <configuration>
+ <doapOptions>
+ <programmingLanguage>Java</programmingLanguage>
+ <category>library</category>
+ </doapOptions>
+ <asfExtOptions>
+ <charter>
+ The Apache Logging Services Project creates and maintains open-source software related to the logging of
+ application behavior and released at no charge to the public.
+ </charter>
+ </asfExtOptions>
+ </configuration>
+ <executions>
+ <execution>
+ <id>site</id>
+ <phase>site</phase>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <reporting>
+ <plugins>
+ <!-- Changes report -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-changes-plugin</artifactId>
+ <version>${changes.plugin.version}</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>changes-report</report>
+ <report>jira-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <statusIds>Resolved, Closed</statusIds>
+ <columnNames>Type,Key,Summary,Assignee,Status,Resolution,Fix Version</columnNames>
+ <useJql>true</useJql>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-project-info-reports-plugin</artifactId>
+ <version>2.8.1</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>index</report>
+ <report>dependencies</report>
+ <report>dependency-info</report>
+ <report>dependency-convergence</report>
+ <report>dependency-management</report>
+ <report>project-team</report>
+ <report>mailing-list</report>
+ <report>issue-tracking</report>
+ <report>license</report>
+ <report>scm</report>
+ <report>summary</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <!-- you'd think these would be the defaults, right? -->
+ <customBundle>${project.basedir}/src/site/custom/project-info-report.properties</customBundle>
+ <webAccessUrl>${project.scm.url}</webAccessUrl>
+ <anonymousConnection>${project.scm.connection}</anonymousConnection>
+ <developerConnection>${project.scm.developerConnection}</developerConnection>
+ <scmTag>log4j-${Log4jReleaseVersion}</scmTag>
+ </configuration>
+ </plugin>
+ <!-- Surefire report -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ <version>${surefire.plugin.version}</version>
+ <reportSets>
+ <reportSet>
+ <id>integration-tests</id>
+ <reports>
+ <report>failsafe-report-only</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ <!-- RAT report -->
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <version>${rat.plugin.version}</version>
+ <configuration>
+ <excludes>
+ <!-- Matches other RAT configurations in this POM -->
+ <exclude>src/main/resources/META-INF/services/**/*</exclude>
+ <!-- IntelliJ files -->
+ <exclude>.idea/**/*</exclude>
+ <exclude>src/test/resources/**/*</exclude>
+ <!-- IDE settings imports -->
+ <exclude>src/ide/**</exclude>
+ <!-- does it even make sense to apply a license to a GPG signature? -->
+ <exclude>**/*.asc</exclude>
+ <!-- jQuery is MIT-licensed, but RAT can't figure it out -->
+ <exclude>src/site/resources/js/jquery.js</exclude>
+ <exclude>src/site/resources/js/jquery.min.js</exclude>
+ <!-- Generated files -->
+ <exclude>log4j-distribution/target/**/*</exclude>
+ <exclude>log4j-distribution/.project</exclude>
+ <exclude>log4j-distribution/.settings/**</exclude>
+ <exclude>velocity.log</exclude>
+ <!-- Other -->
+ <exclude>felix-cache/**</exclude>
+ <exclude>RELEASE-NOTES.txt</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+ <distributionManagement>
+ <downloadUrl>https://logging.apache.org/log4j/2.x/download.html</downloadUrl>
+ <!-- site is only included to make maven-site-plugin stop complaining -->
+ <site>
+ <id>www.example.com</id>
+ <url>scp://www.example.com/www/docs/project/</url>
+ </site>
+ </distributionManagement>
+ <modules>
+ <module>log4j-server</module>
+ </modules>
+ <profiles>
+ <profile>
+ <id>Windows</id>
+ <activation>
+ <os>
+ <family>Windows</family>
+ </os>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <argLine>-server</argLine>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <configuration>
+ <argLine>-server</argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>pdf</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pdf-plugin</artifactId>
+ <version>${pdf.plugin.version}</version>
+ <executions>
+ <execution>
+ <id>pdf</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>pdf</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.reporting.outputDirectory}</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>release-notes</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-changes-plugin</artifactId>
+ <version>${changes.plugin.version}</version>
+ <configuration>
+ <template>announcement.vm</template>
+ <templateDirectory>src/changes</templateDirectory>
+ <runOnlyAtExecutionRoot>true</runOnlyAtExecutionRoot>
+ <announcementDirectory>.</announcementDirectory>
+ <announcementFile>RELEASE-NOTES.txt</announcementFile>
+ <issueManagementSystems>
+ <issueManagementSystem>changes.xml</issueManagementSystem>
+ <!--<issueManagementSystem>JIRA</issueManagementSystem> -->
+ </issueManagementSystems>
+ <version>${Log4jReleaseVersion}</version>
+ <announceParameters>
+ <releaseVersion>${Log4jReleaseVersion}</releaseVersion>
+ <releaseCount>${Log4jReleaseCount}</releaseCount>
+ </announceParameters>
+ <useJql>true</useJql>
+ </configuration>
+ <executions>
+ <execution>
+ <id>create-release-notes</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>announcement-generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>apache-release</id>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>source-release-assembly</id>
+ <configuration>
+ <skipAssembly>true</skipAssembly>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <modules>
+ <module>log4j-distribution</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>rat</id>
+ <build>
+ <plugins>
+ <!-- RAT report -->
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <version>${rat.plugin.version}</version>
+ <configuration>
+ <excludes>
+ <!-- Matches other RAT configurations in this POM -->
+ <exclude>src/main/resources/META-INF/services/**/*</exclude>
+ <!-- IntelliJ files -->
+ <exclude>.idea/**/*</exclude>
+ <exclude>src/test/resources/**/*</exclude>
+ <!-- IDE settings imports -->
+ <exclude>src/ide/**</exclude>
+ <!-- does it even make sense to apply a license to a GPG signature? -->
+ <exclude>**/*.asc</exclude>
+ <!-- jQuery is MIT-licensed, but RAT can't figure it out -->
+ <exclude>src/site/resources/js/jquery.js</exclude>
+ <exclude>src/site/resources/js/jquery.min.js</exclude>
+ <!-- Generated files -->
+ <exclude>log4j-distribution/target/**/*</exclude>
+ <exclude>log4j-distribution/.project</exclude>
+ <exclude>log4j-distribution/.settings/**</exclude>
+ <exclude>velocity.log</exclude>
+ <!-- Other -->
+ <exclude>felix-cache/**</exclude>
+ <exclude>RELEASE-NOTES.md</exclude>
+ </excludes>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <!-- http://www.yourkit.com/docs/80/help/agent.jsp -->
+ <id>yourkit-mac</id>
+ <!--
+ <activation>
+ <os>
+ <family>Mac</family>
+ </os>
+ <file>
+ <exists>${yourkit.home}/bin/mac/libyjpagent.jnilib</exists>
+ </file>
+ </activation>
+ -->
+ <properties>
+ <yourkit.home>/Applications/YJP.app</yourkit.home>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.yourkit</groupId>
+ <artifactId>yjp-controller-api-redist</artifactId>
+ <version>2013</version>
+ <scope>system</scope>
+ <systemPath>${yourkit.home}/lib/yjp-controller-api-redist.jar</systemPath>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <argLine>-agentpath:"${yourkit.home}/bin/mac/libyjpagent.jnilib"</argLine>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <configuration>
+ <argLine>-agentpath:"${yourkit.home}/bin/mac/libyjpagent.jnilib"</argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>jdk7</id>
+ <activation>
+ <jdk>1.7</jdk>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <argLine>-XX:MaxPermSize=512m</argLine>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <configuration>
+ <argLine>-XX:MaxPermSize=512m</argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>java8-doclint-disabled</id>
+ <activation>
+ <jdk>[1.8,)</jdk>
+ </activation>
+ <properties>
+ <javadoc.opts>-Xdoclint:none</javadoc.opts>
+ </properties>
+ </profile>
+ </profiles>
+</project>
diff --git a/src/site/xdoc/runtime-dependencies.xml b/src/site/xdoc/runtime-dependencies.xml
new file mode 100644
index 0000000..1df89a6
--- /dev/null
+++ b/src/site/xdoc/runtime-dependencies.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
+
+ <properties>
+ <title>Log4j Tools Runtime Dependencies</title>
+ </properties>
+
+ <body>
+ <section name="Log4j Tools Runtime Dependencies">
+ <p>
+ Some Log4J features depend on external libraries. This page lists the required and optional
+ dependencies.
+ </p>
+ <p>
+ As of version 2.4, Log4J requires Java 7.
+ </p>
+ <p>
+ Log4j version 2.3 and older require Java 6.
+ </p>
+
+ <a name="log4j-server" />
+ <h4>log4j-server</h4>
+ <table>
+ <caption align="top">Optional Dependencies per Feature in Log4J Server components</caption>
+ <tr>
+ <th>Feature</th>
+ <th>Requirements</th>
+ </tr>
+ <tr>
+ <td>JMS receivers</td>
+ <td>a JMS broker like <a href="http://activemq.apache.org/">Apache ActiveMQ</a></td>
+ </tr>
+ </table>
+
+ </section>
+ </body>
+</document>