You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2016/09/08 14:51:09 UTC

[40/50] [abbrv] logging-log4j2 git commit: org.apache.logging.log4j.core.appender.OutputStreamManager.closeOutputStream() now returns a boolean to signal whether there was a problem closing any resouce.

org.apache.logging.log4j.core.appender.OutputStreamManager.closeOutputStream()
now returns a boolean to signal whether there was a problem closing any
resouce.

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

Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 97e45ed63b19eb1bc99facacd2b9be01c003c335
Parents: 41ac242
Author: ggregory <gg...@US-L-GG02.rocketsoftware.com>
Authored: Wed Sep 7 13:02:54 2016 -0400
Committer: ggregory <gg...@US-L-GG02.rocketsoftware.com>
Committed: Wed Sep 7 13:02:54 2016 -0400

----------------------------------------------------------------------
 .../core/appender/MemoryMappedFileManager.java  | 716 ++++++++++---------
 .../core/appender/OutputStreamManager.java      | 686 +++++++++---------
 .../core/appender/RandomAccessFileManager.java  | 430 +++++------
 .../rolling/RollingRandomAccessFileManager.java | 528 +++++++-------
 .../log4j/core/net/TcpSocketManager.java        | 652 ++++++++---------
 5 files changed, 1511 insertions(+), 1501 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/97e45ed6/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java
index a856610..332e8e4 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java
@@ -1,357 +1,359 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.appender;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.io.Serializable;
-import java.lang.reflect.Method;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.util.Closer;
-import org.apache.logging.log4j.core.util.NullOutputStream;
-
-//Lines too long...
-//CHECKSTYLE:OFF
-/**
- * Extends OutputStreamManager but instead of using a buffered output stream, this class maps a region of a file into
- * memory and writes to this memory region.
- * <p>
- *
- * @see <a href="http://www.codeproject.com/Tips/683614/Things-to-Know-about-Memory-Mapped-File-in-Java">
- *      http://www.codeproject.com/Tips/683614/Things-to-Know-about-Memory-Mapped-File-in-Java</a>
- * @see <a href="http://bugs.java.com/view_bug.do?bug_id=6893654">http://bugs.java.com/view_bug.do?bug_id=6893654</a>
- * @see <a href="http://bugs.java.com/view_bug.do?bug_id=4724038">http://bugs.java.com/view_bug.do?bug_id=4724038</a>
- * @see <a
- *      href="http://stackoverflow.com/questions/9261316/memory-mapped-mappedbytebuffer-or-direct-bytebuffer-for-db-implementation">
- *      http://stackoverflow.com/questions/9261316/memory-mapped-mappedbytebuffer-or-direct-bytebuffer-for-db-implementation</a>
- *
- * @since 2.1
- */
-//CHECKSTYLE:ON
-public class MemoryMappedFileManager extends OutputStreamManager {
-    /**
-     * Default length of region to map.
-     */
-    static final int DEFAULT_REGION_LENGTH = 32 * 1024 * 1024;
-    private static final int MAX_REMAP_COUNT = 10;
-    private static final MemoryMappedFileManagerFactory FACTORY = new MemoryMappedFileManagerFactory();
-    private static final double NANOS_PER_MILLISEC = 1000.0 * 1000.0;
-
-    private final boolean isForce;
-    private final int regionLength;
-    private final String advertiseURI;
-    private final RandomAccessFile randomAccessFile;
-    private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
-    private MappedByteBuffer mappedBuffer;
-    private long mappingOffset;
-
-    protected MemoryMappedFileManager(final RandomAccessFile file, final String fileName, final OutputStream os,
-            final boolean force, final long position, final int regionLength, final String advertiseURI,
-            final Layout<? extends Serializable> layout, final boolean writeHeader) throws IOException {
-        super(os, fileName, layout, writeHeader, ByteBuffer.wrap(new byte[0]));
-        this.isForce = force;
-        this.randomAccessFile = Objects.requireNonNull(file, "RandomAccessFile");
-        this.regionLength = regionLength;
-        this.advertiseURI = advertiseURI;
-        this.isEndOfBatch.set(Boolean.FALSE);
-        this.mappedBuffer = mmap(randomAccessFile.getChannel(), getFileName(), position, regionLength);
-        this.byteBuffer = mappedBuffer;
-        this.mappingOffset = position;
-    }
-
-    /**
-     * Returns the MemoryMappedFileManager.
-     *
-     * @param fileName The name of the file to manage.
-     * @param append true if the file should be appended to, false if it should be overwritten.
-     * @param isForce true if the contents should be flushed to disk on every write
-     * @param regionLength The mapped region length.
-     * @param advertiseURI the URI to use when advertising the file
-     * @param layout The layout.
-     * @return A MemoryMappedFileManager for the File.
-     */
-    public static MemoryMappedFileManager getFileManager(final String fileName, final boolean append,
-            final boolean isForce, final int regionLength, final String advertiseURI,
-            final Layout<? extends Serializable> layout) {
-        return (MemoryMappedFileManager) getManager(fileName, new FactoryData(append, isForce, regionLength,
-                advertiseURI, layout), FACTORY);
-    }
-
-    public Boolean isEndOfBatch() {
-        return isEndOfBatch.get();
-    }
-
-    public void setEndOfBatch(final boolean endOfBatch) {
-        this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
-    }
-
-    @Override
-    protected synchronized void write(final byte[] bytes, int offset, int length, final boolean immediateFlush) {
-        while (length > mappedBuffer.remaining()) {
-            final int chunk = mappedBuffer.remaining();
-            mappedBuffer.put(bytes, offset, chunk);
-            offset += chunk;
-            length -= chunk;
-            remap();
-        }
-        mappedBuffer.put(bytes, offset, length);
-
-        // no need to call flush() if force is true,
-        // already done in AbstractOutputStreamAppender.append
-    }
-
-    private synchronized void remap() {
-        final long offset = this.mappingOffset + mappedBuffer.position();
-        final int length = mappedBuffer.remaining() + regionLength;
-        try {
-            unsafeUnmap(mappedBuffer);
-            final long fileLength = randomAccessFile.length() + regionLength;
-            LOGGER.debug("{} {} extending {} by {} bytes to {}", getClass().getSimpleName(), getName(), getFileName(),
-                    regionLength, fileLength);
-
-            final long startNanos = System.nanoTime();
-            randomAccessFile.setLength(fileLength);
-            final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
-            LOGGER.debug("{} {} extended {} OK in {} millis", getClass().getSimpleName(), getName(), getFileName(),
-                    millis);
-
-            mappedBuffer = mmap(randomAccessFile.getChannel(), getFileName(), offset, length);
-            this.byteBuffer = mappedBuffer;
-            mappingOffset = offset;
-        } catch (final Exception ex) {
-            logError("Unable to remap", ex);
-        }
-    }
-
-    @Override
-    public synchronized void flush() {
-        mappedBuffer.force();
-    }
-
-    @Override
-    public synchronized void closeOutputStream() {
-        final long position = mappedBuffer.position();
-        final long length = mappingOffset + position;
-        try {
-            unsafeUnmap(mappedBuffer);
-        } catch (final Exception ex) {
-            logError("Unable to unmap MappedBuffer", ex);
-        }
-        try {
-            LOGGER.debug("MMapAppender closing. Setting {} length to {} (offset {} + position {})", getFileName(),
-                    length, mappingOffset, position);
-            randomAccessFile.setLength(length);
-            randomAccessFile.close();
-        } catch (final IOException ex) {
-            logError("Unable to close MemoryMappedFile", ex);
-        }
-    }
-
-    public static MappedByteBuffer mmap(final FileChannel fileChannel, final String fileName, final long start,
-            final int size) throws IOException {
-        for (int i = 1;; i++) {
-            try {
-                LOGGER.debug("MMapAppender remapping {} start={}, size={}", fileName, start, size);
-
-                final long startNanos = System.nanoTime();
-                final MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, start, size);
-                map.order(ByteOrder.nativeOrder());
-
-                final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
-                LOGGER.debug("MMapAppender remapped {} OK in {} millis", fileName, millis);
-
-                return map;
-            } catch (final IOException e) {
-                if (e.getMessage() == null || !e.getMessage().endsWith("user-mapped section open")) {
-                    throw e;
-                }
-                LOGGER.debug("Remap attempt {}/{} failed. Retrying...", i, MAX_REMAP_COUNT, e);
-                if (i < MAX_REMAP_COUNT) {
-                    Thread.yield();
-                } else {
-                    try {
-                        Thread.sleep(1);
-                    } catch (final InterruptedException ignored) {
-                        Thread.currentThread().interrupt();
-                        throw e;
-                    }
-                }
-            }
-        }
-    }
-
-    private static void unsafeUnmap(final MappedByteBuffer mbb) throws PrivilegedActionException {
-        LOGGER.debug("MMapAppender unmapping old buffer...");
-        final long startNanos = System.nanoTime();
-        AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
-            @Override
-            public Object run() throws Exception {
-                final Method getCleanerMethod = mbb.getClass().getMethod("cleaner");
-                getCleanerMethod.setAccessible(true);
-                final Object cleaner = getCleanerMethod.invoke(mbb); // sun.misc.Cleaner instance
-                final Method cleanMethod = cleaner.getClass().getMethod("clean");
-                cleanMethod.invoke(cleaner);
-                return null;
-            }
-        });
-        final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
-        LOGGER.debug("MMapAppender unmapped buffer OK in {} millis", millis);
-    }
-
-    /**
-     * Returns the name of the File being managed.
-     *
-     * @return The name of the File being managed.
-     */
-    public String getFileName() {
-        return getName();
-    }
-
-    /**
-     * Returns the length of the memory mapped region.
-     *
-     * @return the length of the mapped region
-     */
-    public int getRegionLength() {
-        return regionLength;
-    }
-
-    /**
-     * Returns {@code true} if the content of the buffer should be forced to the storage device on every write,
-     * {@code false} otherwise.
-     *
-     * @return whether each write should be force-sync'ed
-     */
-    public boolean isImmediateFlush() {
-        return isForce;
-    }
-
-    /**
-     * Gets this FileManager's content format specified by:
-     * <p>
-     * Key: "fileURI" Value: provided "advertiseURI" param.
-     * </p>
-     *
-     * @return Map of content format keys supporting FileManager
-     */
-    @Override
-    public Map<String, String> getContentFormat() {
-        final Map<String, String> result = new HashMap<>(super.getContentFormat());
-        result.put("fileURI", advertiseURI);
-        return result;
-    }
-
-    @Override
-    protected void flushBuffer(final ByteBuffer buffer) {
-        // do nothing (do not call drain() to avoid spurious remapping)
-    }
-
-    @Override
-    public ByteBuffer getByteBuffer() {
-        return mappedBuffer;
-    }
-
-    @Override
-    public ByteBuffer drain(final ByteBuffer buf) {
-        remap();
-        return mappedBuffer;
-    }
-
-    /**
-     * Factory Data.
-     */
-    private static class FactoryData {
-        private final boolean append;
-        private final boolean force;
-        private final int regionLength;
-        private final String advertiseURI;
-        private final Layout<? extends Serializable> layout;
-
-        /**
-         * Constructor.
-         *
-         * @param append Append to existing file or truncate.
-         * @param force forces the memory content to be written to the storage device on every event
-         * @param regionLength length of the mapped region
-         * @param advertiseURI the URI to use when advertising the file
-         * @param layout The layout.
-         */
-        public FactoryData(final boolean append, final boolean force, final int regionLength,
-                final String advertiseURI, final Layout<? extends Serializable> layout) {
-            this.append = append;
-            this.force = force;
-            this.regionLength = regionLength;
-            this.advertiseURI = advertiseURI;
-            this.layout = layout;
-        }
-    }
-
-    /**
-     * Factory to create a MemoryMappedFileManager.
-     */
-    private static class MemoryMappedFileManagerFactory
-            implements ManagerFactory<MemoryMappedFileManager, FactoryData> {
-
-        /**
-         * Create a MemoryMappedFileManager.
-         *
-         * @param name The name of the File.
-         * @param data The FactoryData
-         * @return The MemoryMappedFileManager for the File.
-         */
-        @SuppressWarnings("resource")
-        @Override
-        public MemoryMappedFileManager createManager(final String name, final FactoryData data) {
-            final File file = new File(name);
-            final File parent = file.getParentFile();
-            if (null != parent && !parent.exists()) {
-                parent.mkdirs();
-            }
-            if (!data.append) {
-                file.delete();
-            }
-
-            final boolean writeHeader = !data.append || !file.exists();
-            final OutputStream os = NullOutputStream.NULL_OUTPUT_STREAM;
-            RandomAccessFile raf = null;
-            try {
-                raf = new RandomAccessFile(name, "rw");
-                final long position = (data.append) ? raf.length() : 0;
-                raf.setLength(position + data.regionLength);
-                return new MemoryMappedFileManager(raf, name, os, data.force, position, data.regionLength,
-                        data.advertiseURI, data.layout, writeHeader);
-            } catch (final Exception ex) {
-                LOGGER.error("MemoryMappedFileManager (" + name + ") " + ex, ex);
-                Closer.closeSilently(raf);
-            }
-            return null;
-        }
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.util.Closer;
+import org.apache.logging.log4j.core.util.NullOutputStream;
+
+//Lines too long...
+//CHECKSTYLE:OFF
+/**
+ * Extends OutputStreamManager but instead of using a buffered output stream, this class maps a region of a file into
+ * memory and writes to this memory region.
+ * <p>
+ *
+ * @see <a href="http://www.codeproject.com/Tips/683614/Things-to-Know-about-Memory-Mapped-File-in-Java">
+ *      http://www.codeproject.com/Tips/683614/Things-to-Know-about-Memory-Mapped-File-in-Java</a>
+ * @see <a href="http://bugs.java.com/view_bug.do?bug_id=6893654">http://bugs.java.com/view_bug.do?bug_id=6893654</a>
+ * @see <a href="http://bugs.java.com/view_bug.do?bug_id=4724038">http://bugs.java.com/view_bug.do?bug_id=4724038</a>
+ * @see <a
+ *      href="http://stackoverflow.com/questions/9261316/memory-mapped-mappedbytebuffer-or-direct-bytebuffer-for-db-implementation">
+ *      http://stackoverflow.com/questions/9261316/memory-mapped-mappedbytebuffer-or-direct-bytebuffer-for-db-implementation</a>
+ *
+ * @since 2.1
+ */
+//CHECKSTYLE:ON
+public class MemoryMappedFileManager extends OutputStreamManager {
+    /**
+     * Default length of region to map.
+     */
+    static final int DEFAULT_REGION_LENGTH = 32 * 1024 * 1024;
+    private static final int MAX_REMAP_COUNT = 10;
+    private static final MemoryMappedFileManagerFactory FACTORY = new MemoryMappedFileManagerFactory();
+    private static final double NANOS_PER_MILLISEC = 1000.0 * 1000.0;
+
+    private final boolean isForce;
+    private final int regionLength;
+    private final String advertiseURI;
+    private final RandomAccessFile randomAccessFile;
+    private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
+    private MappedByteBuffer mappedBuffer;
+    private long mappingOffset;
+
+    protected MemoryMappedFileManager(final RandomAccessFile file, final String fileName, final OutputStream os,
+            final boolean force, final long position, final int regionLength, final String advertiseURI,
+            final Layout<? extends Serializable> layout, final boolean writeHeader) throws IOException {
+        super(os, fileName, layout, writeHeader, ByteBuffer.wrap(new byte[0]));
+        this.isForce = force;
+        this.randomAccessFile = Objects.requireNonNull(file, "RandomAccessFile");
+        this.regionLength = regionLength;
+        this.advertiseURI = advertiseURI;
+        this.isEndOfBatch.set(Boolean.FALSE);
+        this.mappedBuffer = mmap(randomAccessFile.getChannel(), getFileName(), position, regionLength);
+        this.byteBuffer = mappedBuffer;
+        this.mappingOffset = position;
+    }
+
+    /**
+     * Returns the MemoryMappedFileManager.
+     *
+     * @param fileName The name of the file to manage.
+     * @param append true if the file should be appended to, false if it should be overwritten.
+     * @param isForce true if the contents should be flushed to disk on every write
+     * @param regionLength The mapped region length.
+     * @param advertiseURI the URI to use when advertising the file
+     * @param layout The layout.
+     * @return A MemoryMappedFileManager for the File.
+     */
+    public static MemoryMappedFileManager getFileManager(final String fileName, final boolean append,
+            final boolean isForce, final int regionLength, final String advertiseURI,
+            final Layout<? extends Serializable> layout) {
+        return (MemoryMappedFileManager) getManager(fileName, new FactoryData(append, isForce, regionLength,
+                advertiseURI, layout), FACTORY);
+    }
+
+    public Boolean isEndOfBatch() {
+        return isEndOfBatch.get();
+    }
+
+    public void setEndOfBatch(final boolean endOfBatch) {
+        this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
+    }
+
+    @Override
+    protected synchronized void write(final byte[] bytes, int offset, int length, final boolean immediateFlush) {
+        while (length > mappedBuffer.remaining()) {
+            final int chunk = mappedBuffer.remaining();
+            mappedBuffer.put(bytes, offset, chunk);
+            offset += chunk;
+            length -= chunk;
+            remap();
+        }
+        mappedBuffer.put(bytes, offset, length);
+
+        // no need to call flush() if force is true,
+        // already done in AbstractOutputStreamAppender.append
+    }
+
+    private synchronized void remap() {
+        final long offset = this.mappingOffset + mappedBuffer.position();
+        final int length = mappedBuffer.remaining() + regionLength;
+        try {
+            unsafeUnmap(mappedBuffer);
+            final long fileLength = randomAccessFile.length() + regionLength;
+            LOGGER.debug("{} {} extending {} by {} bytes to {}", getClass().getSimpleName(), getName(), getFileName(),
+                    regionLength, fileLength);
+
+            final long startNanos = System.nanoTime();
+            randomAccessFile.setLength(fileLength);
+            final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
+            LOGGER.debug("{} {} extended {} OK in {} millis", getClass().getSimpleName(), getName(), getFileName(),
+                    millis);
+
+            mappedBuffer = mmap(randomAccessFile.getChannel(), getFileName(), offset, length);
+            this.byteBuffer = mappedBuffer;
+            mappingOffset = offset;
+        } catch (final Exception ex) {
+            logError("Unable to remap", ex);
+        }
+    }
+
+    @Override
+    public synchronized void flush() {
+        mappedBuffer.force();
+    }
+
+    @Override
+    public synchronized boolean closeOutputStream() {
+        final long position = mappedBuffer.position();
+        final long length = mappingOffset + position;
+        try {
+            unsafeUnmap(mappedBuffer);
+        } catch (final Exception ex) {
+            logError("Unable to unmap MappedBuffer", ex);
+        }
+        try {
+            LOGGER.debug("MMapAppender closing. Setting {} length to {} (offset {} + position {})", getFileName(),
+                    length, mappingOffset, position);
+            randomAccessFile.setLength(length);
+            randomAccessFile.close();
+            return true;
+        } catch (final IOException ex) {
+            logError("Unable to close MemoryMappedFile", ex);
+            return false;
+        }
+    }
+
+    public static MappedByteBuffer mmap(final FileChannel fileChannel, final String fileName, final long start,
+            final int size) throws IOException {
+        for (int i = 1;; i++) {
+            try {
+                LOGGER.debug("MMapAppender remapping {} start={}, size={}", fileName, start, size);
+
+                final long startNanos = System.nanoTime();
+                final MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, start, size);
+                map.order(ByteOrder.nativeOrder());
+
+                final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
+                LOGGER.debug("MMapAppender remapped {} OK in {} millis", fileName, millis);
+
+                return map;
+            } catch (final IOException e) {
+                if (e.getMessage() == null || !e.getMessage().endsWith("user-mapped section open")) {
+                    throw e;
+                }
+                LOGGER.debug("Remap attempt {}/{} failed. Retrying...", i, MAX_REMAP_COUNT, e);
+                if (i < MAX_REMAP_COUNT) {
+                    Thread.yield();
+                } else {
+                    try {
+                        Thread.sleep(1);
+                    } catch (final InterruptedException ignored) {
+                        Thread.currentThread().interrupt();
+                        throw e;
+                    }
+                }
+            }
+        }
+    }
+
+    private static void unsafeUnmap(final MappedByteBuffer mbb) throws PrivilegedActionException {
+        LOGGER.debug("MMapAppender unmapping old buffer...");
+        final long startNanos = System.nanoTime();
+        AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+            @Override
+            public Object run() throws Exception {
+                final Method getCleanerMethod = mbb.getClass().getMethod("cleaner");
+                getCleanerMethod.setAccessible(true);
+                final Object cleaner = getCleanerMethod.invoke(mbb); // sun.misc.Cleaner instance
+                final Method cleanMethod = cleaner.getClass().getMethod("clean");
+                cleanMethod.invoke(cleaner);
+                return null;
+            }
+        });
+        final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
+        LOGGER.debug("MMapAppender unmapped buffer OK in {} millis", millis);
+    }
+
+    /**
+     * Returns the name of the File being managed.
+     *
+     * @return The name of the File being managed.
+     */
+    public String getFileName() {
+        return getName();
+    }
+
+    /**
+     * Returns the length of the memory mapped region.
+     *
+     * @return the length of the mapped region
+     */
+    public int getRegionLength() {
+        return regionLength;
+    }
+
+    /**
+     * Returns {@code true} if the content of the buffer should be forced to the storage device on every write,
+     * {@code false} otherwise.
+     *
+     * @return whether each write should be force-sync'ed
+     */
+    public boolean isImmediateFlush() {
+        return isForce;
+    }
+
+    /**
+     * Gets this FileManager's content format specified by:
+     * <p>
+     * Key: "fileURI" Value: provided "advertiseURI" param.
+     * </p>
+     *
+     * @return Map of content format keys supporting FileManager
+     */
+    @Override
+    public Map<String, String> getContentFormat() {
+        final Map<String, String> result = new HashMap<>(super.getContentFormat());
+        result.put("fileURI", advertiseURI);
+        return result;
+    }
+
+    @Override
+    protected void flushBuffer(final ByteBuffer buffer) {
+        // do nothing (do not call drain() to avoid spurious remapping)
+    }
+
+    @Override
+    public ByteBuffer getByteBuffer() {
+        return mappedBuffer;
+    }
+
+    @Override
+    public ByteBuffer drain(final ByteBuffer buf) {
+        remap();
+        return mappedBuffer;
+    }
+
+    /**
+     * Factory Data.
+     */
+    private static class FactoryData {
+        private final boolean append;
+        private final boolean force;
+        private final int regionLength;
+        private final String advertiseURI;
+        private final Layout<? extends Serializable> layout;
+
+        /**
+         * Constructor.
+         *
+         * @param append Append to existing file or truncate.
+         * @param force forces the memory content to be written to the storage device on every event
+         * @param regionLength length of the mapped region
+         * @param advertiseURI the URI to use when advertising the file
+         * @param layout The layout.
+         */
+        public FactoryData(final boolean append, final boolean force, final int regionLength,
+                final String advertiseURI, final Layout<? extends Serializable> layout) {
+            this.append = append;
+            this.force = force;
+            this.regionLength = regionLength;
+            this.advertiseURI = advertiseURI;
+            this.layout = layout;
+        }
+    }
+
+    /**
+     * Factory to create a MemoryMappedFileManager.
+     */
+    private static class MemoryMappedFileManagerFactory
+            implements ManagerFactory<MemoryMappedFileManager, FactoryData> {
+
+        /**
+         * Create a MemoryMappedFileManager.
+         *
+         * @param name The name of the File.
+         * @param data The FactoryData
+         * @return The MemoryMappedFileManager for the File.
+         */
+        @SuppressWarnings("resource")
+        @Override
+        public MemoryMappedFileManager createManager(final String name, final FactoryData data) {
+            final File file = new File(name);
+            final File parent = file.getParentFile();
+            if (null != parent && !parent.exists()) {
+                parent.mkdirs();
+            }
+            if (!data.append) {
+                file.delete();
+            }
+
+            final boolean writeHeader = !data.append || !file.exists();
+            final OutputStream os = NullOutputStream.NULL_OUTPUT_STREAM;
+            RandomAccessFile raf = null;
+            try {
+                raf = new RandomAccessFile(name, "rw");
+                final long position = (data.append) ? raf.length() : 0;
+                raf.setLength(position + data.regionLength);
+                return new MemoryMappedFileManager(raf, name, os, data.force, position, data.regionLength,
+                        data.advertiseURI, data.layout, writeHeader);
+            } catch (final Exception ex) {
+                LOGGER.error("MemoryMappedFileManager (" + name + ") " + ex, ex);
+                Closer.closeSilently(raf);
+            }
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/97e45ed6/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
index af4e11e..c5b7a45 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
@@ -1,342 +1,344 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.appender;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.Serializable;
-import java.nio.ByteBuffer;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.layout.ByteBufferDestination;
-import org.apache.logging.log4j.core.util.Constants;
-
-/**
- * Manages an OutputStream so that it can be shared by multiple Appenders and will
- * allow appenders to reconfigure without requiring a new stream.
- */
-public class OutputStreamManager extends AbstractManager implements ByteBufferDestination {
-    protected final Layout<?> layout;
-    protected ByteBuffer byteBuffer;
-    private volatile OutputStream os;
-    private boolean skipFooter;
-
-    protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
-            final boolean writeHeader) {
-        // Can't use new ctor because it throws an exception
-        this(os, streamName, layout, writeHeader, Constants.ENCODER_BYTE_BUFFER_SIZE);
-    }
-
-    protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
-            final boolean writeHeader, final int bufferSize) {
-        // Can't use new ctor because it throws an exception
-        this(os, streamName, layout, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
-    }
-
-    /**
-     * @since 2.6
-     * @deprecated
-     */
-    @Deprecated
-    protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
-            final boolean writeHeader, final ByteBuffer byteBuffer) {
-        super(null, streamName);
-        this.os = os;
-        this.layout = layout;
-        if (writeHeader && layout != null) {
-            final byte[] header = layout.getHeader();
-            if (header != null) {
-                try {
-                    getOutputStream().write(header, 0, header.length);
-                } catch (final IOException e) {
-                    logError("Unable to write header", e);
-                }
-            }
-        }
-        this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
-    }
-
-    /**
-     * @since 2.7
-     */
-    protected OutputStreamManager(final LoggerContext loggerContext, final OutputStream os, final String streamName,
-            final boolean createOnDemand, final Layout<? extends Serializable> layout, final boolean writeHeader,
-            final ByteBuffer byteBuffer) {
-        super(loggerContext, streamName);
-        if (createOnDemand && os != null) {
-            LOGGER.error(
-                    "Invalid OutputStreamManager configuration for '{}': You cannot both set the OutputStream and request on-demand.",
-                    streamName);
-        }
-        this.layout = layout;
-        this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
-        this.os = os;
-        if (writeHeader && layout != null) {
-            final byte[] header = layout.getHeader();
-            if (header != null) {
-                try {
-                    getOutputStream().write(header, 0, header.length);
-                } catch (final IOException e) {
-                    logError("Unable to write header for " + streamName, e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Creates a Manager.
-     *
-     * @param name The name of the stream to manage.
-     * @param data The data to pass to the Manager.
-     * @param factory The factory to use to create the Manager.
-     * @param <T> The type of the OutputStreamManager.
-     * @return An OutputStreamManager.
-     */
-    public static <T> OutputStreamManager getManager(final String name, final T data,
-                                                 final ManagerFactory<? extends OutputStreamManager, T> factory) {
-        return AbstractManager.getManager(name, factory, data);
-    }
-
-    @SuppressWarnings("unused")
-    protected OutputStream createOutputStream() throws IOException {
-        throw new IllegalStateException(getClass().getCanonicalName() + " must implement createOutputStream()");
-    }
-    
-    /**
-     * Indicate whether the footer should be skipped or not.
-     * @param skipFooter true if the footer should be skipped.
-     */
-    public void skipFooter(final boolean skipFooter) {
-        this.skipFooter = skipFooter;
-    }
-
-    /**
-     * Default hook to write footer during close.
-     */
-    @Override
-    public void releaseSub(final long timeout, final TimeUnit timeUnit) {
-        writeFooter();
-        closeOutputStream();
-    }
-
-    /**
-     * Writes the footer.
-     */
-    protected void writeFooter() {
-        if (layout == null || skipFooter) {
-            return;
-        }
-        final byte[] footer = layout.getFooter();
-        if (footer != null) {
-            write(footer);
-        }
-    }
-
-    /**
-     * Returns the status of the stream.
-     * @return true if the stream is open, false if it is not.
-     */
-    public boolean isOpen() {
-        return getCount() > 0;
-    }
-
-    protected OutputStream getOutputStream() throws IOException {
-        if (os == null) {
-            os = createOutputStream();
-        }
-        return os;
-    }
-
-    protected void setOutputStream(final OutputStream os) {
-        final byte[] header = layout.getHeader();
-        if (header != null) {
-            try {
-                os.write(header, 0, header.length);
-                this.os = os; // only update field if os.write() succeeded
-            } catch (final IOException ioe) {
-                logError("Unable to write header", ioe);
-            }
-        } else {
-            this.os = os;
-        }
-    }
-
-    /**
-     * Some output streams synchronize writes while others do not.
-     * @param bytes The serialized Log event.
-     * @throws AppenderLoggingException if an error occurs.
-     */
-    protected void write(final byte[] bytes)  {
-        write(bytes, 0, bytes.length, false);
-    }
-
-    /**
-     * Some output streams synchronize writes while others do not.
-     * @param bytes The serialized Log event.
-     * @param immediateFlush If true, flushes after writing.
-     * @throws AppenderLoggingException if an error occurs.
-     */
-    protected void write(final byte[] bytes, final boolean immediateFlush)  {
-        write(bytes, 0, bytes.length, immediateFlush);
-    }
-
-    /**
-     * Some output streams synchronize writes while others do not. Synchronizing here insures that
-     * log events won't be intertwined.
-     * @param bytes The serialized Log event.
-     * @param offset The offset into the byte array.
-     * @param length The number of bytes to write.
-     * @throws AppenderLoggingException if an error occurs.
-     */
-    protected void write(final byte[] bytes, final int offset, final int length) {
-        write(bytes, offset, length, false);
-    }
-
-    /**
-     * Some output streams synchronize writes while others do not. Synchronizing here insures that
-     * log events won't be intertwined.
-     * @param bytes The serialized Log event.
-     * @param offset The offset into the byte array.
-     * @param length The number of bytes to write.
-     * @param immediateFlush flushes immediately after writing.
-     * @throws AppenderLoggingException if an error occurs.
-     */
-    protected synchronized void write(final byte[] bytes, final int offset, final int length, final boolean immediateFlush) {
-        if (immediateFlush && byteBuffer.position() == 0) {
-            writeToDestination(bytes, offset, length);
-            flushDestination();
-            return;
-        }
-        if (length >= byteBuffer.capacity()) {
-            // if request length exceeds buffer capacity, flush the buffer and write the data directly
-            flush();
-            writeToDestination(bytes, offset, length);
-        } else {
-            if (length > byteBuffer.remaining()) {
-                flush();
-            }
-            byteBuffer.put(bytes, offset, length);
-        }
-        if (immediateFlush) {
-            flush();
-        }
-    }
-
-    /**
-     * Writes the specified section of the specified byte array to the stream.
-     *
-     * @param bytes the array containing data
-     * @param offset from where to write
-     * @param length how many bytes to write
-     * @since 2.6
-     */
-    protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
-        try {
-            getOutputStream().write(bytes, offset, length);
-        } catch (final IOException ex) {
-            throw new AppenderLoggingException("Error writing to stream " + getName(), ex);
-        }
-    }
-
-    /**
-     * Calls {@code flush()} on the underlying output stream.
-     * @since 2.6
-     */
-    protected synchronized void flushDestination() {
-        final OutputStream stream = os; // access volatile field only once per method
-        if (stream != null) {
-            try {
-                stream.flush();
-            } catch (final IOException ex) {
-                throw new AppenderLoggingException("Error flushing stream " + getName(), ex);
-            }
-        }
-    }
-
-    /**
-     * Drains the ByteBufferDestination's buffer into the destination. By default this calls
-     * {@link OutputStreamManager#write(byte[], int, int, boolean)} with the buffer contents.
-     * The underlying stream is not {@linkplain OutputStream#flush() flushed}.
-     *
-     * @see #flushDestination()
-     * @since 2.6
-     */
-    protected synchronized void flushBuffer(final ByteBuffer buf) {
-        buf.flip();
-        if (buf.limit() > 0) {
-            writeToDestination(buf.array(), 0, buf.limit());
-        }
-        buf.clear();
-    }
-
-    /**
-     * Flushes any buffers.
-     */
-    public synchronized void flush() {
-        flushBuffer(byteBuffer);
-        flushDestination();
-    }
-
-    protected synchronized void closeOutputStream() {
-        flush();
-        final OutputStream stream = os; // access volatile field only once per method
-        if (stream == null || stream == System.out || stream == System.err) {
-            return;
-        }
-        try {
-            stream.close();
-        } catch (final IOException ex) {
-            logError("Unable to close stream", ex);
-        }
-    }
-
-    /**
-     * Returns this {@code ByteBufferDestination}'s buffer.
-     * @return the buffer
-     * @since 2.6
-     */
-    @Override
-    public ByteBuffer getByteBuffer() {
-        return byteBuffer;
-    }
-
-    /**
-     * Drains the ByteBufferDestination's buffer into the destination. By default this calls
-     * {@link #flushBuffer(ByteBuffer)} with the specified buffer. Subclasses may override.
-     * <p>
-     * Do not call this method lightly! For some subclasses this is a very expensive operation. For example,
-     * {@link MemoryMappedFileManager} will assume this method was called because the end of the mapped region
-     * was reached during a text encoding operation and will {@linkplain MemoryMappedFileManager#remap() remap} its
-     * buffer.
-     * </p><p>
-     * To just flush the buffered contents to the underlying stream, call
-     * {@link #flushBuffer(ByteBuffer)} directly instead.
-     * </p>
-     *
-     * @param buf the buffer whose contents to write the the destination
-     * @return the specified buffer
-     * @since 2.6
-     */
-    @Override
-    public ByteBuffer drain(final ByteBuffer buf) {
-        flushBuffer(buf);
-        return buf;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.layout.ByteBufferDestination;
+import org.apache.logging.log4j.core.util.Constants;
+
+/**
+ * Manages an OutputStream so that it can be shared by multiple Appenders and will
+ * allow appenders to reconfigure without requiring a new stream.
+ */
+public class OutputStreamManager extends AbstractManager implements ByteBufferDestination {
+    protected final Layout<?> layout;
+    protected ByteBuffer byteBuffer;
+    private volatile OutputStream os;
+    private boolean skipFooter;
+
+    protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
+            final boolean writeHeader) {
+        // Can't use new ctor because it throws an exception
+        this(os, streamName, layout, writeHeader, Constants.ENCODER_BYTE_BUFFER_SIZE);
+    }
+
+    protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
+            final boolean writeHeader, final int bufferSize) {
+        // Can't use new ctor because it throws an exception
+        this(os, streamName, layout, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
+    }
+
+    /**
+     * @since 2.6
+     * @deprecated
+     */
+    @Deprecated
+    protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
+            final boolean writeHeader, final ByteBuffer byteBuffer) {
+        super(null, streamName);
+        this.os = os;
+        this.layout = layout;
+        if (writeHeader && layout != null) {
+            final byte[] header = layout.getHeader();
+            if (header != null) {
+                try {
+                    getOutputStream().write(header, 0, header.length);
+                } catch (final IOException e) {
+                    logError("Unable to write header", e);
+                }
+            }
+        }
+        this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
+    }
+
+    /**
+     * @since 2.7
+     */
+    protected OutputStreamManager(final LoggerContext loggerContext, final OutputStream os, final String streamName,
+            final boolean createOnDemand, final Layout<? extends Serializable> layout, final boolean writeHeader,
+            final ByteBuffer byteBuffer) {
+        super(loggerContext, streamName);
+        if (createOnDemand && os != null) {
+            LOGGER.error(
+                    "Invalid OutputStreamManager configuration for '{}': You cannot both set the OutputStream and request on-demand.",
+                    streamName);
+        }
+        this.layout = layout;
+        this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
+        this.os = os;
+        if (writeHeader && layout != null) {
+            final byte[] header = layout.getHeader();
+            if (header != null) {
+                try {
+                    getOutputStream().write(header, 0, header.length);
+                } catch (final IOException e) {
+                    logError("Unable to write header for " + streamName, e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates a Manager.
+     *
+     * @param name The name of the stream to manage.
+     * @param data The data to pass to the Manager.
+     * @param factory The factory to use to create the Manager.
+     * @param <T> The type of the OutputStreamManager.
+     * @return An OutputStreamManager.
+     */
+    public static <T> OutputStreamManager getManager(final String name, final T data,
+                                                 final ManagerFactory<? extends OutputStreamManager, T> factory) {
+        return AbstractManager.getManager(name, factory, data);
+    }
+
+    @SuppressWarnings("unused")
+    protected OutputStream createOutputStream() throws IOException {
+        throw new IllegalStateException(getClass().getCanonicalName() + " must implement createOutputStream()");
+    }
+    
+    /**
+     * Indicate whether the footer should be skipped or not.
+     * @param skipFooter true if the footer should be skipped.
+     */
+    public void skipFooter(final boolean skipFooter) {
+        this.skipFooter = skipFooter;
+    }
+
+    /**
+     * Default hook to write footer during close.
+     */
+    @Override
+    public void releaseSub(final long timeout, final TimeUnit timeUnit) {
+        writeFooter();
+        closeOutputStream();
+    }
+
+    /**
+     * Writes the footer.
+     */
+    protected void writeFooter() {
+        if (layout == null || skipFooter) {
+            return;
+        }
+        final byte[] footer = layout.getFooter();
+        if (footer != null) {
+            write(footer);
+        }
+    }
+
+    /**
+     * Returns the status of the stream.
+     * @return true if the stream is open, false if it is not.
+     */
+    public boolean isOpen() {
+        return getCount() > 0;
+    }
+
+    protected OutputStream getOutputStream() throws IOException {
+        if (os == null) {
+            os = createOutputStream();
+        }
+        return os;
+    }
+
+    protected void setOutputStream(final OutputStream os) {
+        final byte[] header = layout.getHeader();
+        if (header != null) {
+            try {
+                os.write(header, 0, header.length);
+                this.os = os; // only update field if os.write() succeeded
+            } catch (final IOException ioe) {
+                logError("Unable to write header", ioe);
+            }
+        } else {
+            this.os = os;
+        }
+    }
+
+    /**
+     * Some output streams synchronize writes while others do not.
+     * @param bytes The serialized Log event.
+     * @throws AppenderLoggingException if an error occurs.
+     */
+    protected void write(final byte[] bytes)  {
+        write(bytes, 0, bytes.length, false);
+    }
+
+    /**
+     * Some output streams synchronize writes while others do not.
+     * @param bytes The serialized Log event.
+     * @param immediateFlush If true, flushes after writing.
+     * @throws AppenderLoggingException if an error occurs.
+     */
+    protected void write(final byte[] bytes, final boolean immediateFlush)  {
+        write(bytes, 0, bytes.length, immediateFlush);
+    }
+
+    /**
+     * Some output streams synchronize writes while others do not. Synchronizing here insures that
+     * log events won't be intertwined.
+     * @param bytes The serialized Log event.
+     * @param offset The offset into the byte array.
+     * @param length The number of bytes to write.
+     * @throws AppenderLoggingException if an error occurs.
+     */
+    protected void write(final byte[] bytes, final int offset, final int length) {
+        write(bytes, offset, length, false);
+    }
+
+    /**
+     * Some output streams synchronize writes while others do not. Synchronizing here insures that
+     * log events won't be intertwined.
+     * @param bytes The serialized Log event.
+     * @param offset The offset into the byte array.
+     * @param length The number of bytes to write.
+     * @param immediateFlush flushes immediately after writing.
+     * @throws AppenderLoggingException if an error occurs.
+     */
+    protected synchronized void write(final byte[] bytes, final int offset, final int length, final boolean immediateFlush) {
+        if (immediateFlush && byteBuffer.position() == 0) {
+            writeToDestination(bytes, offset, length);
+            flushDestination();
+            return;
+        }
+        if (length >= byteBuffer.capacity()) {
+            // if request length exceeds buffer capacity, flush the buffer and write the data directly
+            flush();
+            writeToDestination(bytes, offset, length);
+        } else {
+            if (length > byteBuffer.remaining()) {
+                flush();
+            }
+            byteBuffer.put(bytes, offset, length);
+        }
+        if (immediateFlush) {
+            flush();
+        }
+    }
+
+    /**
+     * Writes the specified section of the specified byte array to the stream.
+     *
+     * @param bytes the array containing data
+     * @param offset from where to write
+     * @param length how many bytes to write
+     * @since 2.6
+     */
+    protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
+        try {
+            getOutputStream().write(bytes, offset, length);
+        } catch (final IOException ex) {
+            throw new AppenderLoggingException("Error writing to stream " + getName(), ex);
+        }
+    }
+
+    /**
+     * Calls {@code flush()} on the underlying output stream.
+     * @since 2.6
+     */
+    protected synchronized void flushDestination() {
+        final OutputStream stream = os; // access volatile field only once per method
+        if (stream != null) {
+            try {
+                stream.flush();
+            } catch (final IOException ex) {
+                throw new AppenderLoggingException("Error flushing stream " + getName(), ex);
+            }
+        }
+    }
+
+    /**
+     * Drains the ByteBufferDestination's buffer into the destination. By default this calls
+     * {@link OutputStreamManager#write(byte[], int, int, boolean)} with the buffer contents.
+     * The underlying stream is not {@linkplain OutputStream#flush() flushed}.
+     *
+     * @see #flushDestination()
+     * @since 2.6
+     */
+    protected synchronized void flushBuffer(final ByteBuffer buf) {
+        buf.flip();
+        if (buf.limit() > 0) {
+            writeToDestination(buf.array(), 0, buf.limit());
+        }
+        buf.clear();
+    }
+
+    /**
+     * Flushes any buffers.
+     */
+    public synchronized void flush() {
+        flushBuffer(byteBuffer);
+        flushDestination();
+    }
+
+    protected synchronized boolean closeOutputStream() {
+        flush();
+        final OutputStream stream = os; // access volatile field only once per method
+        if (stream == null || stream == System.out || stream == System.err) {
+            return true;
+        }
+        try {
+            stream.close();
+        } catch (final IOException ex) {
+            logError("Unable to close stream", ex);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns this {@code ByteBufferDestination}'s buffer.
+     * @return the buffer
+     * @since 2.6
+     */
+    @Override
+    public ByteBuffer getByteBuffer() {
+        return byteBuffer;
+    }
+
+    /**
+     * Drains the ByteBufferDestination's buffer into the destination. By default this calls
+     * {@link #flushBuffer(ByteBuffer)} with the specified buffer. Subclasses may override.
+     * <p>
+     * Do not call this method lightly! For some subclasses this is a very expensive operation. For example,
+     * {@link MemoryMappedFileManager} will assume this method was called because the end of the mapped region
+     * was reached during a text encoding operation and will {@linkplain MemoryMappedFileManager#remap() remap} its
+     * buffer.
+     * </p><p>
+     * To just flush the buffered contents to the underlying stream, call
+     * {@link #flushBuffer(ByteBuffer)} directly instead.
+     * </p>
+     *
+     * @param buf the buffer whose contents to write the the destination
+     * @return the specified buffer
+     * @since 2.6
+     */
+    @Override
+    public ByteBuffer drain(final ByteBuffer buf) {
+        flushBuffer(buf);
+        return buf;
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/97e45ed6/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RandomAccessFileManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RandomAccessFileManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RandomAccessFileManager.java
index 68a6f81..4cd5459 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RandomAccessFileManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RandomAccessFileManager.java
@@ -1,214 +1,216 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.appender;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.io.Serializable;
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.util.NullOutputStream;
-
-/**
- * Extends OutputStreamManager but instead of using a buffered output stream,
- * this class uses a {@code ByteBuffer} and a {@code RandomAccessFile} to do the
- * I/O.
- */
-public class RandomAccessFileManager extends OutputStreamManager {
-    static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
-
-    private static final RandomAccessFileManagerFactory FACTORY = new RandomAccessFileManagerFactory();
-
-    private final String advertiseURI;
-    private final RandomAccessFile randomAccessFile;
-    private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
-
-    protected RandomAccessFileManager(final LoggerContext loggerContext, final RandomAccessFile file, final String fileName,
-            final OutputStream os, final int bufferSize, final String advertiseURI,
-            final Layout<? extends Serializable> layout, final boolean writeHeader) {
-        super(loggerContext, os, fileName, false, layout, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
-        this.randomAccessFile = file;
-        this.advertiseURI = advertiseURI;
-        this.isEndOfBatch.set(Boolean.FALSE);
-    }
-
-    /**
-     * Returns the RandomAccessFileManager.
-     *
-     * @param fileName The name of the file to manage.
-     * @param append true if the file should be appended to, false if it should
-     *            be overwritten.
-     * @param isFlush true if the contents should be flushed to disk on every
-     *            write
-     * @param bufferSize The buffer size.
-     * @param advertiseURI the URI to use when advertising the file
-     * @param layout The layout.
-     * @param configuration The configuration.
-     * @return A RandomAccessFileManager for the File.
-     */
-    public static RandomAccessFileManager getFileManager(final String fileName, final boolean append,
-            final boolean isFlush, final int bufferSize, final String advertiseURI,
-            final Layout<? extends Serializable> layout, final Configuration configuration) {
-        return (RandomAccessFileManager) getManager(fileName, new FactoryData(append,
-                isFlush, bufferSize, advertiseURI, layout, configuration), FACTORY);
-    }
-
-    public Boolean isEndOfBatch() {
-        return isEndOfBatch.get();
-    }
-
-    public void setEndOfBatch(final boolean endOfBatch) {
-        this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
-    }
-
-    @Override
-    protected void writeToDestination(final byte[] bytes, final int offset, final int length) {
-        try {
-            randomAccessFile.write(bytes, offset, length);
-        } catch (final IOException ex) {
-            final String msg = "Error writing to RandomAccessFile " + getName();
-            throw new AppenderLoggingException(msg, ex);
-        }
-    }
-
-    @Override
-    public synchronized void flush() {
-        flushBuffer(byteBuffer);
-    }
-
-    @Override
-    public synchronized void closeOutputStream() {
-        flush();
-        try {
-            randomAccessFile.close();
-        } catch (final IOException ex) {
-            logError("Unable to close RandomAccessFile", ex);
-        }
-    }
-
-    /**
-     * Returns the name of the File being managed.
-     *
-     * @return The name of the File being managed.
-     */
-    public String getFileName() {
-        return getName();
-    }
-
-    /**
-     * Returns the buffer capacity.
-     * @return the buffer size
-     */
-    public int getBufferSize() {
-        return byteBuffer.capacity();
-    }
-
-    /**
-     * Gets this FileManager's content format specified by:
-     * <p>
-     * Key: "fileURI" Value: provided "advertiseURI" param.
-     * </p>
-     *
-     * @return Map of content format keys supporting FileManager
-     */
-    @Override
-    public Map<String, String> getContentFormat() {
-        final Map<String, String> result = new HashMap<>(
-                super.getContentFormat());
-        result.put("fileURI", advertiseURI);
-        return result;
-    }
-
-    /**
-     * Factory Data.
-     */
-    private static class FactoryData extends ConfigurationFactoryData {
-        private final boolean append;
-        private final boolean immediateFlush;
-        private final int bufferSize;
-        private final String advertiseURI;
-        private final Layout<? extends Serializable> layout;
-
-        /**
-         * Constructor.
-         *
-         * @param append Append status.
-         * @param bufferSize size of the buffer
-         * @param configuration The configuration.
-         */
-        public FactoryData(final boolean append, final boolean immediateFlush, final int bufferSize,
-                final String advertiseURI, final Layout<? extends Serializable> layout, final Configuration configuration) {
-            super(configuration);
-            this.append = append;
-            this.immediateFlush = immediateFlush;
-            this.bufferSize = bufferSize;
-            this.advertiseURI = advertiseURI;
-            this.layout = layout;
-        }
-    }
-
-    /**
-     * Factory to create a RandomAccessFileManager.
-     */
-    private static class RandomAccessFileManagerFactory implements
-            ManagerFactory<RandomAccessFileManager, FactoryData> {
-
-        /**
-         * Create a RandomAccessFileManager.
-         *
-         * @param name The name of the File.
-         * @param data The FactoryData
-         * @return The RandomAccessFileManager for the File.
-         */
-        @Override
-        public RandomAccessFileManager createManager(final String name, final FactoryData data) {
-            final File file = new File(name);
-            final File parent = file.getParentFile();
-            if (null != parent && !parent.exists()) {
-                parent.mkdirs();
-            }
-            if (!data.append) {
-                file.delete();
-            }
-
-            final boolean writeHeader = !data.append || !file.exists();
-            final OutputStream os = NullOutputStream.NULL_OUTPUT_STREAM;
-            RandomAccessFile raf;
-            try {
-                raf = new RandomAccessFile(name, "rw");
-                if (data.append) {
-                    raf.seek(raf.length());
-                } else {
-                    raf.setLength(0);
-                }
-                return new RandomAccessFileManager(data.getLoggerContext(), raf, name,
-                        os, data.bufferSize, data.advertiseURI, data.layout, writeHeader);
-            } catch (final Exception ex) {
-                LOGGER.error("RandomAccessFileManager (" + name + ") " + ex, ex);
-            }
-            return null;
-        }
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.util.NullOutputStream;
+
+/**
+ * Extends OutputStreamManager but instead of using a buffered output stream,
+ * this class uses a {@code ByteBuffer} and a {@code RandomAccessFile} to do the
+ * I/O.
+ */
+public class RandomAccessFileManager extends OutputStreamManager {
+    static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
+
+    private static final RandomAccessFileManagerFactory FACTORY = new RandomAccessFileManagerFactory();
+
+    private final String advertiseURI;
+    private final RandomAccessFile randomAccessFile;
+    private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
+
+    protected RandomAccessFileManager(final LoggerContext loggerContext, final RandomAccessFile file, final String fileName,
+            final OutputStream os, final int bufferSize, final String advertiseURI,
+            final Layout<? extends Serializable> layout, final boolean writeHeader) {
+        super(loggerContext, os, fileName, false, layout, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
+        this.randomAccessFile = file;
+        this.advertiseURI = advertiseURI;
+        this.isEndOfBatch.set(Boolean.FALSE);
+    }
+
+    /**
+     * Returns the RandomAccessFileManager.
+     *
+     * @param fileName The name of the file to manage.
+     * @param append true if the file should be appended to, false if it should
+     *            be overwritten.
+     * @param isFlush true if the contents should be flushed to disk on every
+     *            write
+     * @param bufferSize The buffer size.
+     * @param advertiseURI the URI to use when advertising the file
+     * @param layout The layout.
+     * @param configuration The configuration.
+     * @return A RandomAccessFileManager for the File.
+     */
+    public static RandomAccessFileManager getFileManager(final String fileName, final boolean append,
+            final boolean isFlush, final int bufferSize, final String advertiseURI,
+            final Layout<? extends Serializable> layout, final Configuration configuration) {
+        return (RandomAccessFileManager) getManager(fileName, new FactoryData(append,
+                isFlush, bufferSize, advertiseURI, layout, configuration), FACTORY);
+    }
+
+    public Boolean isEndOfBatch() {
+        return isEndOfBatch.get();
+    }
+
+    public void setEndOfBatch(final boolean endOfBatch) {
+        this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
+    }
+
+    @Override
+    protected void writeToDestination(final byte[] bytes, final int offset, final int length) {
+        try {
+            randomAccessFile.write(bytes, offset, length);
+        } catch (final IOException ex) {
+            final String msg = "Error writing to RandomAccessFile " + getName();
+            throw new AppenderLoggingException(msg, ex);
+        }
+    }
+
+    @Override
+    public synchronized void flush() {
+        flushBuffer(byteBuffer);
+    }
+
+    @Override
+    public synchronized boolean closeOutputStream() {
+        flush();
+        try {
+            randomAccessFile.close();
+            return true;
+        } catch (final IOException ex) {
+            logError("Unable to close RandomAccessFile", ex);
+            return false;
+        }
+    }
+
+    /**
+     * Returns the name of the File being managed.
+     *
+     * @return The name of the File being managed.
+     */
+    public String getFileName() {
+        return getName();
+    }
+
+    /**
+     * Returns the buffer capacity.
+     * @return the buffer size
+     */
+    public int getBufferSize() {
+        return byteBuffer.capacity();
+    }
+
+    /**
+     * Gets this FileManager's content format specified by:
+     * <p>
+     * Key: "fileURI" Value: provided "advertiseURI" param.
+     * </p>
+     *
+     * @return Map of content format keys supporting FileManager
+     */
+    @Override
+    public Map<String, String> getContentFormat() {
+        final Map<String, String> result = new HashMap<>(
+                super.getContentFormat());
+        result.put("fileURI", advertiseURI);
+        return result;
+    }
+
+    /**
+     * Factory Data.
+     */
+    private static class FactoryData extends ConfigurationFactoryData {
+        private final boolean append;
+        private final boolean immediateFlush;
+        private final int bufferSize;
+        private final String advertiseURI;
+        private final Layout<? extends Serializable> layout;
+
+        /**
+         * Constructor.
+         *
+         * @param append Append status.
+         * @param bufferSize size of the buffer
+         * @param configuration The configuration.
+         */
+        public FactoryData(final boolean append, final boolean immediateFlush, final int bufferSize,
+                final String advertiseURI, final Layout<? extends Serializable> layout, final Configuration configuration) {
+            super(configuration);
+            this.append = append;
+            this.immediateFlush = immediateFlush;
+            this.bufferSize = bufferSize;
+            this.advertiseURI = advertiseURI;
+            this.layout = layout;
+        }
+    }
+
+    /**
+     * Factory to create a RandomAccessFileManager.
+     */
+    private static class RandomAccessFileManagerFactory implements
+            ManagerFactory<RandomAccessFileManager, FactoryData> {
+
+        /**
+         * Create a RandomAccessFileManager.
+         *
+         * @param name The name of the File.
+         * @param data The FactoryData
+         * @return The RandomAccessFileManager for the File.
+         */
+        @Override
+        public RandomAccessFileManager createManager(final String name, final FactoryData data) {
+            final File file = new File(name);
+            final File parent = file.getParentFile();
+            if (null != parent && !parent.exists()) {
+                parent.mkdirs();
+            }
+            if (!data.append) {
+                file.delete();
+            }
+
+            final boolean writeHeader = !data.append || !file.exists();
+            final OutputStream os = NullOutputStream.NULL_OUTPUT_STREAM;
+            RandomAccessFile raf;
+            try {
+                raf = new RandomAccessFile(name, "rw");
+                if (data.append) {
+                    raf.seek(raf.length());
+                } else {
+                    raf.setLength(0);
+                }
+                return new RandomAccessFileManager(data.getLoggerContext(), raf, name,
+                        os, data.bufferSize, data.advertiseURI, data.layout, writeHeader);
+            } catch (final Exception ex) {
+                LOGGER.error("RandomAccessFileManager (" + name + ") " + ex, ex);
+            }
+            return null;
+        }
+    }
+
+}