LOG4J2-228 & LOG4J2-246 - UDP now sends 1 event per packet. The data buffer is reset in a finally clause

git-svn-id: https://svn.apache.org/repos/asf/logging/log4j/log4j2/trunk@1487289 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/src/main/java/org/apache/logging/log4j/core/appender/DefaultErrorHandler.java b/core/src/main/java/org/apache/logging/log4j/core/appender/DefaultErrorHandler.java
index 091dcb1..0ed1ce6 100644
--- a/core/src/main/java/org/apache/logging/log4j/core/appender/DefaultErrorHandler.java
+++ b/core/src/main/java/org/apache/logging/log4j/core/appender/DefaultErrorHandler.java
@@ -69,7 +69,7 @@
             LOGGER.error(msg, t);
         }
         lastException = current;
-        if (!appender.isExceptionSuppressed() && t != null) {
+        if (!appender.isExceptionSuppressed() && t != null && !(t instanceof AppenderRuntimeException)) {
             throw new AppenderRuntimeException(msg, t);
         }
     }
@@ -87,7 +87,7 @@
             LOGGER.error(msg, t);
         }
         lastException = current;
-        if (!appender.isExceptionSuppressed() && t != null) {
+        if (!appender.isExceptionSuppressed() && t != null && !(t instanceof AppenderRuntimeException)) {
             throw new AppenderRuntimeException(msg, t);
         }
     }
diff --git a/core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java b/core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java
index 6dd56f7..f9d0665 100644
--- a/core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java
+++ b/core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java
@@ -98,7 +98,7 @@
                                                 @PluginAttr("advertise") final String advertise,
                                                 @PluginConfiguration final Configuration config) {
 
-        final boolean isFlush = immediateFlush == null ? true : Boolean.valueOf(immediateFlush);
+        boolean isFlush = immediateFlush == null ? true : Boolean.valueOf(immediateFlush);
         boolean isAdvertise = advertise == null ? false : Boolean.valueOf(advertise);
         final boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
         final boolean fail = immediateFail == null ? true : Boolean.valueOf(immediateFail);
@@ -116,8 +116,12 @@
         }
 
         final String prot = protocol != null ? protocol : Protocol.TCP.name();
+        final Protocol p = EnglishEnums.valueOf(Protocol.class, protocol);
+        if (p.equals(Protocol.UDP)) {
+            isFlush = true;
+        }
 
-        final AbstractSocketManager manager = createSocketManager(prot, host, port, reconnectDelay, fail, layout);
+        final AbstractSocketManager manager = createSocketManager(p, host, port, reconnectDelay, fail, layout);
         if (manager == null) {
             return null;
         }
@@ -126,10 +130,9 @@
                 isAdvertise ? config.getAdvertiser() : null);
     }
 
-    protected static AbstractSocketManager createSocketManager(final String protocol, final String host, final int port,
+    protected static AbstractSocketManager createSocketManager(final Protocol p, final String host, final int port,
                                                                final int delay, final boolean immediateFail,
                                                                final Layout layout) {
-        final Protocol p = EnglishEnums.valueOf(Protocol.class, protocol);
         switch (p) {
             case TCP:
                 return TCPSocketManager.getSocketManager(host, port, delay, immediateFail, layout);
diff --git a/core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java b/core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java
index 55aff97..fe52f9d 100644
--- a/core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java
+++ b/core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java
@@ -29,6 +29,7 @@
 import org.apache.logging.log4j.core.net.AbstractSocketManager;
 import org.apache.logging.log4j.core.net.Advertiser;
 import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.util.EnglishEnums;
 
 import java.io.Serializable;
 
@@ -130,7 +131,8 @@
             return null;
         }
         final String prot = protocol != null ? protocol : Protocol.UDP.name();
-        final AbstractSocketManager manager = createSocketManager(prot, host, port, reconnectDelay, fail, layout);
+        final Protocol p = EnglishEnums.valueOf(Protocol.class, protocol);
+        final AbstractSocketManager manager = createSocketManager(p, host, port, reconnectDelay, fail, layout);
         if (manager == null) {
             return null;
         }
diff --git a/core/src/main/java/org/apache/logging/log4j/core/net/DatagramOutputStream.java b/core/src/main/java/org/apache/logging/log4j/core/net/DatagramOutputStream.java
index 2fe3d9a..9846ee2 100644
--- a/core/src/main/java/org/apache/logging/log4j/core/net/DatagramOutputStream.java
+++ b/core/src/main/java/org/apache/logging/log4j/core/net/DatagramOutputStream.java
@@ -48,13 +48,18 @@
 
     private byte[] data;
 
+    private final byte[] header;
+    private final byte[] footer;
+
     /**
      * The Constructor.
      * @param host The host to connect to.
      * @param port The port on the host.
      */
-    public DatagramOutputStream(final String host, final int port) {
+    public DatagramOutputStream(final String host, final int port, final byte[] header, final byte[] footer) {
         this.port = port;
+        this.header = header;
+        this.footer = footer;
         try {
             address = InetAddress.getByName(host);
         } catch (final UnknownHostException ex) {
@@ -89,11 +94,20 @@
 
     @Override
     public synchronized void flush() throws IOException {
-        if (this.data != null && this.ds != null && this.address != null) {
-            final DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
-            ds.send(packet);
+        try {
+            if (this.data != null && this.ds != null && this.address != null) {
+                if (footer != null) {
+                    copy(footer, 0, footer.length);
+                }
+                final DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
+                ds.send(packet);
+            }
+        } finally {
+            data = null;
+            if (header != null) {
+                copy(header, 0, header.length);
+            }
         }
-        data = null;
     }
 
     @Override
@@ -111,7 +125,7 @@
         final int index = data == null ? 0 : data.length;
         final byte[] copy = new byte[length + index];
         if (data != null) {
-            System.arraycopy(data, 0, copy, 0, index);
+            System.arraycopy(data, 0, copy, 0, data.length);
         }
         System.arraycopy(bytes, offset, copy, index, length);
         data = copy;
diff --git a/core/src/main/java/org/apache/logging/log4j/core/net/DatagramSocketManager.java b/core/src/main/java/org/apache/logging/log4j/core/net/DatagramSocketManager.java
index 12b578c..633e758 100644
--- a/core/src/main/java/org/apache/logging/log4j/core/net/DatagramSocketManager.java
+++ b/core/src/main/java/org/apache/logging/log4j/core/net/DatagramSocketManager.java
@@ -104,7 +104,8 @@
         @Override
         public DatagramSocketManager createManager(final String name, final FactoryData data) {
             InetAddress address;
-            final OutputStream os = new DatagramOutputStream(data.host, data.port);
+            final OutputStream os = new DatagramOutputStream(data.host, data.port, data.layout.getHeader(),
+                data.layout.getFooter());
             try {
                 address = InetAddress.getByName(data.host);
             } catch (final UnknownHostException ex) {
diff --git a/core/src/test/java/org/apache/logging/log4j/core/net/UDPSocketServer.java b/core/src/main/java/org/apache/logging/log4j/core/net/UDPSocketServer.java
similarity index 73%
rename from core/src/test/java/org/apache/logging/log4j/core/net/UDPSocketServer.java
rename to core/src/main/java/org/apache/logging/log4j/core/net/UDPSocketServer.java
index 12d2ead..b7b946a 100644
--- a/core/src/test/java/org/apache/logging/log4j/core/net/UDPSocketServer.java
+++ b/core/src/main/java/org/apache/logging/log4j/core/net/UDPSocketServer.java
@@ -57,14 +57,12 @@
 
     private final DatagramSocket server;
 
-    private final ConcurrentMap<Long, DatagramPacketHandler> handlers = new ConcurrentHashMap<Long, DatagramPacketHandler>();
-
     // max size so we only have to deal with one packet
-    private int maxBufferSize = 1024 * 65;
+    private int maxBufferSize = 1024 * 65 + 1024;
 
     /**
      * Constructor.
-     * 
+     *
      * @param port
      *            to listen on.
      * @throws IOException
@@ -80,7 +78,7 @@
 
     /**
      * Main startup for the server.
-     * 
+     *
      * @param args
      *            The command line arguments.
      * @throws Exception
@@ -138,63 +136,19 @@
                 byte[] buf = new byte[maxBufferSize];
                 DatagramPacket packet = new DatagramPacket(buf, buf.length);
                 server.receive(packet);
-                final DatagramPacketHandler handler = new DatagramPacketHandler(packet);
-                handlers.put(handler.getId(), handler);
-                handler.start();
-            } catch (final IOException ioe) {
-                System.out.println("Exception encountered on accept. Ignoring. Stack Trace :");
-                ioe.printStackTrace();
-            }
-        }
-        for (final Map.Entry<Long, DatagramPacketHandler> entry : handlers.entrySet()) {
-            final DatagramPacketHandler handler = entry.getValue();
-            handler.shutdown();
-            try {
-                handler.join();
-            } catch (final InterruptedException ie) {
-                // Ignore the exception
-            }
-        }
-    }
-
-    /**
-     * Thread that processes the events.
-     */
-    private class DatagramPacketHandler extends Thread {
-        private final ObjectInputStream ois;
-        private boolean shutdown = false;
-
-        public DatagramPacketHandler(final DatagramPacket packet) throws IOException {
-            this.ois = new ObjectInputStream(new ByteArrayInputStream(packet.getData(), packet.getOffset(), packet.getLength()));
-        }
-
-        public void shutdown() {
-            this.shutdown = true;
-            interrupt();
-        }
-
-        @Override
-        public void run() {
-            boolean closed = false;
-            try {
-                try {
-                    while (!shutdown) {
-                        final LogEvent event = (LogEvent) ois.readObject();
-                        if (event != null) {
-                            log(event);
-                        }
-                    }
-                } catch (final EOFException eof) {
-                    closed = true;
-                } catch (final OptionalDataException opt) {
-                    logger.error("OptionalDataException eof=" + opt.eof + " length=" + opt.length, opt);
-                } catch (final ClassNotFoundException cnfe) {
-                    logger.error("Unable to locate LogEvent class", cnfe);
-                } catch (final IOException ioe) {
-                    logger.error("IOException encountered while reading from socket", ioe);
+                ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(packet.getData(), packet.getOffset(), packet.getLength()));
+                final LogEvent event = (LogEvent) ois.readObject();
+                if (event != null) {
+                    log(event);
                 }
-            } finally {
-                handlers.remove(getId());
+            } catch (final OptionalDataException opt) {
+                logger.error("OptionalDataException eof=" + opt.eof + " length=" + opt.length, opt);
+            } catch (final ClassNotFoundException cnfe) {
+                logger.error("Unable to locate LogEvent class", cnfe);
+            } catch (final EOFException eofe) {
+                logger.info("EOF encountered");
+            } catch (final IOException ioe) {
+                logger.error("Exception encountered on accept. Ignoring. Stack Trace :", ioe);
             }
         }
     }
diff --git a/core/src/test/java/org/apache/logging/log4j/core/net/AbstractSocketServerTest.java b/core/src/test/java/org/apache/logging/log4j/core/net/AbstractSocketServerTest.java
index 507b4ef..22f47b2 100644
--- a/core/src/test/java/org/apache/logging/log4j/core/net/AbstractSocketServerTest.java
+++ b/core/src/test/java/org/apache/logging/log4j/core/net/AbstractSocketServerTest.java
@@ -23,6 +23,7 @@
 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.AppenderRuntimeException;
 import org.apache.logging.log4j.core.appender.ConsoleAppender;
 import org.apache.logging.log4j.core.appender.SocketAppender;
 import org.apache.logging.log4j.core.filter.AbstractFilter;
@@ -31,6 +32,7 @@
 import org.junit.After;
 import org.junit.Test;
 
+import java.io.IOException;
 import java.io.Serializable;
 import java.util.Arrays;
 import java.util.List;
@@ -63,12 +65,14 @@
 
     private final String port;
     private final String protocol;
+    private final boolean expectLengthException;
 
     private final Logger root = ctx.getLogger(AbstractSocketServerTest.class.getSimpleName());
 
-    protected AbstractSocketServerTest(final String protocol, final String port) {
+    protected AbstractSocketServerTest(final String protocol, final String port, final boolean expectLengthException) {
         this.protocol = protocol;
         this.port = port;
+        this.expectLengthException = expectLengthException;
     }
 
     @After
@@ -87,20 +91,30 @@
         Arrays.fill(a64K, 'a');
         final String m1 = new String(a64K);
         final String m2 = MESSAGE_2 + m1;
-        testServer(m1, m2);
+        if (expectLengthException) {
+            try {
+                testServer(m1, m2);
+
+            } catch (AppenderRuntimeException are) {
+                assertTrue("", are.getCause() != null && are.getCause() instanceof IOException);
+                // Failure expected.
+            }
+        } else {
+            testServer(m1, m2);
+        }
     }
 
     protected void testServer(final String message1, final String message2) throws Exception {
         final Filter socketFilter = new ThreadFilter(Filter.Result.NEUTRAL, Filter.Result.DENY);
         final Filter serverFilter = new ThreadFilter(Filter.Result.DENY, Filter.Result.NEUTRAL);
         final SocketAppender<Serializable> appender = SocketAppender.createAppender("localhost", this.port, this.protocol, "-1", null, "Test", null,
-                null, null, socketFilter, null, null);
+                "false", null, socketFilter, null, null);
         appender.start();
         final ListAppender<LogEvent> listApp = new ListAppender<LogEvent>("Events", serverFilter, null, false, false);
         listApp.start();
         final PatternLayout layout = PatternLayout.createLayout("%m %ex%n", null, null, null, null);
         final ConsoleAppender<String> console = ConsoleAppender.createAppender(layout, null, "SYSTEM_OUT", "Console", "false", "true");
-        final Logger serverLogger = ctx.getLogger(SocketServer.class.getName());
+        final Logger serverLogger = ctx.getLogger(this.getClass().getName());
         serverLogger.addAppender(console);
         serverLogger.setAdditive(false);
 
diff --git a/core/src/test/java/org/apache/logging/log4j/core/net/TCPSocketServerTest.java b/core/src/test/java/org/apache/logging/log4j/core/net/TCPSocketServerTest.java
index 62241bb..5075dd0 100644
--- a/core/src/test/java/org/apache/logging/log4j/core/net/TCPSocketServerTest.java
+++ b/core/src/test/java/org/apache/logging/log4j/core/net/TCPSocketServerTest.java
@@ -47,7 +47,7 @@
     }
 
     public TCPSocketServerTest() {
-        super("tcp", PORT);
+        super("tcp", PORT, false);
     }
 
 }
diff --git a/core/src/test/java/org/apache/logging/log4j/core/net/UDPSocketServerTest.java b/core/src/test/java/org/apache/logging/log4j/core/net/UDPSocketServerTest.java
index 2eddc99..5e83681 100644
--- a/core/src/test/java/org/apache/logging/log4j/core/net/UDPSocketServerTest.java
+++ b/core/src/test/java/org/apache/logging/log4j/core/net/UDPSocketServerTest.java
@@ -22,7 +22,6 @@
 import org.junit.BeforeClass;
 import org.junit.Ignore;
 
-@Ignore
 public class UDPSocketServerTest extends AbstractSocketServerTest {
     private static final String PORT = "8199";
     private static final int PORT_NUM = Integer.parseInt(PORT);
@@ -49,7 +48,7 @@
     }
 
     public UDPSocketServerTest() {
-        super("udp", PORT);
+        super("udp", PORT, true);
     }
 
 }
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 037d029..f22e301 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -22,6 +22,12 @@
   </properties>
   <body>
     <release version="2.0-beta7" date="2013-??-??" description="Bug fixes and enhancements">
+      <action issue="LOG4J2-246" dev="rgoers" type="fix">
+        Data buffer is reset in finally clause.
+      </action>
+      <action issue="LOG4J2-228" dev="rgoers" type="fix">
+        UDP now sends one event per packet.
+      </action>
       <action dev="rpopma" type="update">
         Method name changes in interface org.apache.logging.log4j.spi.ThreadContextMap:
         getContext() to getCopy(), get() to getImmutableMapOrNull().