Repository: logging-log4j2
Updated Branches:
  refs/heads/master 8729b28d0 -> ee87a184c


[LOG4J2-1604] Log4j2 TcpSocketServer in background.

Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/ee87a184
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/ee87a184
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/ee87a184

Branch: refs/heads/master
Commit: ee87a184c7ccbfe03234782ebd6cc76c954a4a3c
Parents: 8729b28
Author: Gary Gregory <ggreg...@apache.org>
Authored: Wed Sep 21 16:19:43 2016 -0700
Committer: Gary Gregory <ggreg...@apache.org>
Committed: Wed Sep 21 16:19:43 2016 -0700

----------------------------------------------------------------------
 log4j-core/pom.xml                              |   6 +
 .../core/net/server/AbstractSocketServer.java   |  93 +++++++++-
 .../log4j/core/net/server/TcpSocketServer.java  | 180 ++++++++++++-------
 .../log4j/core/net/server/UdpSocketServer.java  |  83 ++++-----
 .../log4j/core/util/InetAddressConverter.java   |  19 ++
 pom.xml                                         |   5 +
 src/changes/changes.xml                         |   3 +
 7 files changed, 277 insertions(+), 112 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ee87a184/log4j-core/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-core/pom.xml b/log4j-core/pom.xml
index 6142dad..120404b 100644
--- a/log4j-core/pom.xml
+++ b/log4j-core/pom.xml
@@ -144,6 +144,12 @@
       <artifactId>commons-csv</artifactId>
       <optional>true</optional>
     </dependency>
+    <!-- Command line for TCP and UDP servers -->
+    <dependency>
+      <groupId>com.beust</groupId>
+      <artifactId>jcommander</artifactId>
+      <optional>true</optional>
+    </dependency>
 
     <!-- TEST DEPENDENCIES -->
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ee87a184/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/AbstractSocketServer.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/AbstractSocketServer.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/AbstractSocketServer.java
index 7350e6d..8e24c25 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/AbstractSocketServer.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/AbstractSocketServer.java
@@ -21,6 +21,7 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.InetAddress;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URL;
@@ -34,18 +35,85 @@ 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.InetAddressConverter;
 import org.apache.logging.log4j.core.util.Log4jThread;
 import org.apache.logging.log4j.util.Strings;
 
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.validators.PositiveInteger;
+
 /**
  * Abstract socket server for TCP and UDP implementations.
  * 
- * @param <T> The kind of input stream read
+ * @param <T>
+ *            The kind of input stream read
  * 
- * TODO Make a LifeCycle
+ *            TODO Make a LifeCycle
  */
 public abstract class AbstractSocketServer<T extends InputStream> extends 
LogEventListener implements Runnable {
 
+    protected static class CommandLineArguments {
+
+        @Parameter(names = { "--config", "-c" }, description = "Log4j 
configuration file location (path or URL).")
+        private String configLocation;
+
+        @Parameter(names = { "--help", "-?", "-h" }, help = true, description 
= "Prints this help.")
+        private boolean help;
+
+        @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;
+
+        String getConfigLocation() {
+            return configLocation;
+        }
+
+        int getPort() {
+            return port;
+        }
+
+        boolean isHelp() {
+            return help;
+        }
+
+        protected boolean isInteractive() {
+            return interactive;
+        }
+
+        void setConfigLocation(final String configLocation) {
+            this.configLocation = configLocation;
+        }
+
+        void setHelp(final boolean help) {
+            this.help = help;
+        }
+
+        void setInteractive(final boolean interactive) {
+            this.interactive = interactive;
+        }
+
+        void setPort(final int port) {
+            this.port = port;
+        }
+
+        InetAddress getLocalBindAddress() {
+            return localBindAddress;
+        }
+
+        void setLocalBindAddress(InetAddress localBindAddress) {
+            this.localBindAddress = localBindAddress;
+        }
+    }
+
     /**
      * Factory that creates a Configuration for the server.
      */
@@ -58,7 +126,8 @@ public abstract class AbstractSocketServer<T extends 
InputStream> extends LogEve
         }
 
         @Override
-        public Configuration getConfiguration(final LoggerContext 
loggerContext, final String name, final URI configLocation) {
+        public Configuration getConfiguration(final LoggerContext 
loggerContext, final String name,
+                final URI configLocation) {
             if (Strings.isNotEmpty(path)) {
                 File file = null;
                 ConfigurationSource source = null;
@@ -95,6 +164,18 @@ public abstract class AbstractSocketServer<T extends 
InputStream> extends LogEve
 
     protected static final int MAX_PORT = 65534;
 
+    static <T extends CommandLineArguments> T parseCommandLine(final String[] 
mainArgs, final Class<?> clazz,
+            final T args) {
+        final JCommander jCommander = new JCommander(args);
+        jCommander.setProgramName(clazz.getName());
+        jCommander.setCaseSensitiveOptions(false);
+        jCommander.parse(mainArgs);
+        if (args.isHelp()) {
+            jCommander.usage();
+        }
+        return args;
+    }
+
     private volatile boolean active = true;
 
     protected final LogEventBridge<T> logEventInput;
@@ -104,8 +185,10 @@ public abstract class AbstractSocketServer<T extends 
InputStream> extends LogEve
     /**
      * Creates a new socket server.
      * 
-     * @param port listen to this port
-     * @param logEventInput Use this input to read log events.
+     * @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);

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ee87a184/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/TcpSocketServer.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/TcpSocketServer.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/TcpSocketServer.java
index 6dfce72..72845bf 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/TcpSocketServer.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/TcpSocketServer.java
@@ -23,6 +23,7 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.ObjectInputStream;
 import java.io.OptionalDataException;
+import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.charset.Charset;
@@ -34,14 +35,35 @@ import 
org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.util.Log4jThread;
 import org.apache.logging.log4j.message.EntryMessage;
 
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.validators.PositiveInteger;
+
 /**
- * Listens for events over a socket connection.
+ * 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.
      */
@@ -90,42 +112,6 @@ public class TcpSocketServer<T extends InputStream> extends 
AbstractSocketServer
         }
     }
 
-    private final ConcurrentMap<Long, SocketHandler> handlers = new 
ConcurrentHashMap<>();
-
-    private final ServerSocket serverSocket;
-
-    /**
-     * 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, 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;
-    }
-
     /**
      * Creates a socket server that reads JSON log events.
      * 
@@ -157,6 +143,24 @@ public class TcpSocketServer<T extends InputStream> 
extends AbstractSocketServer
     }
 
     /**
+     * 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,
+            InetAddress localBindAddress) throws IOException {
+        LOGGER.entry(port);
+        final TcpSocketServer<ObjectInputStream> socketServer = new 
TcpSocketServer<>(port, backlog, localBindAddress,
+                new ObjectInputStreamLogEventBridge());
+        return LOGGER.exit(socketServer);
+    }
+
+    /**
      * Creates a socket server that reads XML log events.
      * 
      * @param port
@@ -172,7 +176,7 @@ public class TcpSocketServer<T extends InputStream> extends 
AbstractSocketServer
     }
 
     /**
-     * Main startup for the server.
+     * Main startup for the server. Run with "--help" for to print command 
line help on the console.
      * 
      * @param args
      *        The command line arguments.
@@ -180,38 +184,89 @@ public class TcpSocketServer<T extends InputStream> 
extends AbstractSocketServer
      *         if an error occurs.
      */
     public static void main(final String[] args) throws Exception {
-        if (args.length < 1 || args.length > 2) {
-            System.err.println("Incorrect number of arguments: " + 
args.length);
-            printUsage();
-            return;
-        }
-        final int port = Integer.parseInt(args[0]);
-        if (port <= 0 || port >= MAX_PORT) {
-            System.err.println("Invalid port number: " + port);
-            printUsage();
+        final CommandLineArguments cla = parseCommandLine(args, 
TcpSocketServer.class, new CommandLineArguments());
+        if (cla.isHelp()) {
             return;
         }
-        if (args.length == 2 && args[1].length() > 0) {
-            ConfigurationFactory.setConfigurationFactory(new 
ServerConfigurationFactory(args[1]));
+        if (cla.getConfigLocation() != null) {
+            ConfigurationFactory.setConfigurationFactory(new 
ServerConfigurationFactory(cla.getConfigLocation()));
         }
-        final TcpSocketServer<ObjectInputStream> socketServer = 
TcpSocketServer.createSerializedSocketServer(port);
+        final TcpSocketServer<ObjectInputStream> socketServer = TcpSocketServer
+                .createSerializedSocketServer(cla.getPort(), cla.getBacklog(), 
cla.getLocalBindAddress());
         final Thread serverThread = new Log4jThread(socketServer);
         serverThread.start();
-        final Charset enc = Charset.defaultCharset();
-        final BufferedReader reader = new BufferedReader(new 
InputStreamReader(System.in, enc));
-        while (true) {
-            final String line = reader.readLine();
-            if (line == null || line.equalsIgnoreCase("Quit") || 
line.equalsIgnoreCase("Stop")
-                    || line.equalsIgnoreCase("Exit")) {
-                socketServer.shutdown();
-                serverThread.join();
-                break;
+        if (cla.isInteractive()) {
+            final Charset enc = Charset.defaultCharset();
+            final BufferedReader reader = new BufferedReader(new 
InputStreamReader(System.in, enc));
+            while (true) {
+                final String line = reader.readLine();
+                if (line == null || line.equalsIgnoreCase("Quit") || 
line.equalsIgnoreCase("Stop")
+                        || line.equalsIgnoreCase("Exit")) {
+                    socketServer.shutdown();
+                    serverThread.join();
+                    break;
+                }
             }
         }
     }
 
-    private static void printUsage() {
-        System.out.println("Usage: ServerSocket port configFilePath");
+    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, 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;
     }
 
     /**
@@ -226,8 +281,9 @@ public class TcpSocketServer<T extends InputStream> extends 
AbstractSocketServer
             }
             try {
                 // Accept incoming connections.
-                logger.debug("Socket accept()...");
+                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);
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ee87a184/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/UdpSocketServer.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/UdpSocketServer.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/UdpSocketServer.java
index ce05b14..74829bc 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/UdpSocketServer.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/UdpSocketServer.java
@@ -31,32 +31,14 @@ import 
org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.util.Log4jThread;
 
 /**
- * Listens for events over a socket connection.
+ * 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> {
 
-    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);
-    }
-
     /**
      * Creates a socket server that reads JSON log events.
      * 
@@ -97,7 +79,7 @@ public class UdpSocketServer<T extends InputStream> extends 
AbstractSocketServer
     }
 
     /**
-     * Main startup for the server.
+     * Main startup for the server. Run with "--help" for to print command 
line help on the console.
      * 
      * @param args
      *            The command line arguments.
@@ -105,37 +87,48 @@ public class UdpSocketServer<T extends InputStream> 
extends AbstractSocketServer
      *             if an error occurs.
      */
     public static void main(final String[] args) throws Exception {
-        if (args.length < 1 || args.length > 2) {
-            System.err.println("Incorrect number of arguments: " + 
args.length);
-            printUsage();
+        final CommandLineArguments cla = parseCommandLine(args, 
UdpSocketServer.class, new CommandLineArguments());
+        if (cla.isHelp()) {
             return;
         }
-        final int port = Integer.parseInt(args[0]);
-        if (port <= 0 || port >= MAX_PORT) {
-            System.err.println("Invalid port number:" + port);
-            printUsage();
-            return;
+        if (cla.getConfigLocation() != null) {
+            ConfigurationFactory.setConfigurationFactory(new 
ServerConfigurationFactory(cla.getConfigLocation()));
         }
-        if (args.length == 2 && args[1].length() > 0) {
-            ConfigurationFactory.setConfigurationFactory(new 
ServerConfigurationFactory(args[1]));
-        }
-        final UdpSocketServer<ObjectInputStream> socketServer = 
UdpSocketServer.createSerializedSocketServer(port);
-        final Thread server = new Log4jThread(socketServer);
-        server.start();
-        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")) {
-                socketServer.shutdown();
-                server.join();
-                break;
+        final UdpSocketServer<ObjectInputStream> socketServer = UdpSocketServer
+                .createSerializedSocketServer(cla.getPort());
+        final Thread serverThread = new Log4jThread(socketServer);
+        serverThread.start();
+        if (cla.isInteractive()) {
+            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")) {
+                    socketServer.shutdown();
+                    serverThread.join();
+                    break;
+                }
             }
         }
     }
 
-    private static void printUsage() {
-        System.out.println("Usage: ServerSocket port configFilePath");
+    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);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ee87a184/log4j-core/src/main/java/org/apache/logging/log4j/core/util/InetAddressConverter.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/InetAddressConverter.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/InetAddressConverter.java
new file mode 100644
index 0000000..e14f512
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/InetAddressConverter.java
@@ -0,0 +1,19 @@
+package org.apache.logging.log4j.core.util;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import com.beust.jcommander.IStringConverter;
+
+public class InetAddressConverter implements IStringConverter<InetAddress> {
+
+    @Override
+    public InetAddress convert(String host) {
+        try {
+            return InetAddress.getByName(host);
+        } catch (UnknownHostException e) {
+            throw new IllegalArgumentException(host, e);
+        }
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ee87a184/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 209d34d..b6dab17 100644
--- a/pom.xml
+++ b/pom.xml
@@ -291,6 +291,11 @@
         <version>3.4</version>
       </dependency>
       <dependency>
+        <groupId>com.beust</groupId>
+        <artifactId>jcommander</artifactId>
+        <version>1.48</version>
+      </dependency>
+      <dependency>
         <groupId>ch.qos.logback</groupId>
         <artifactId>logback-classic</artifactId>
         <version>${logbackVersion}</version>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ee87a184/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 4438f1e..036e633 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -258,6 +258,9 @@
       <action issue="LOG4J2-1577" dev="ggregory" type="add">
         Add a Builder to the RoutingAppender and deprecate factory method.
       </action>
+      <action issue="LOG4J2-1604" dev="ggregory" type="update" due-to="Colin 
Hillman">
+        Log4j2 TcpSocketServer in background.
+      </action>
       <action issue="LOG4J2-1574" dev="ggregory" type="update">
         Allow the RollingFileAppender to use default pattern layout.
       </action>

Reply via email to