You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2016/09/07 17:37:53 UTC

[4/7] logging-log4j2 git commit: Properly track resources during shutdown.

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/78fe32c0/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java
index 1fc08a0..db6e9ae 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java
@@ -1,479 +1,479 @@
-/*
- * 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.rolling;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.Serializable;
-import java.nio.ByteBuffer;
-import java.util.concurrent.Future;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
-
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.appender.ConfigurationFactoryData;
-import org.apache.logging.log4j.core.appender.FileManager;
-import org.apache.logging.log4j.core.appender.ManagerFactory;
-import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction;
-import org.apache.logging.log4j.core.appender.rolling.action.Action;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.util.Constants;
-
-/**
- * The Rolling File Manager.
- */
-public class RollingFileManager extends FileManager {
-
-    private static RollingFileManagerFactory factory = new RollingFileManagerFactory();
-
-    protected long size;
-    private long initialTime;
-    private final PatternProcessor patternProcessor;
-    private final Semaphore semaphore = new Semaphore(1);
-    private volatile TriggeringPolicy triggeringPolicy;
-    private volatile RolloverStrategy rolloverStrategy;
-    private volatile boolean renameEmptyFiles = false;
-
-    private static final AtomicReferenceFieldUpdater<RollingFileManager, TriggeringPolicy> triggeringPolicyUpdater =
-            AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, TriggeringPolicy.class, "triggeringPolicy");
-
-    private static final AtomicReferenceFieldUpdater<RollingFileManager, RolloverStrategy> rolloverStrategyUpdater =
-            AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, RolloverStrategy.class, "rolloverStrategy");
-
-    @Deprecated
-    protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
-            final boolean append, final long size, final long time, final TriggeringPolicy triggeringPolicy,
-            final RolloverStrategy rolloverStrategy, final String advertiseURI,
-            final Layout<? extends Serializable> layout, final int bufferSize, final boolean writeHeader) {
-        this(fileName, pattern, os, append, size, time, triggeringPolicy, rolloverStrategy, advertiseURI, layout,
-                writeHeader, ByteBuffer.wrap(new byte[Constants.ENCODER_BYTE_BUFFER_SIZE]));
-    }
-
-    @Deprecated
-    protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
-            final boolean append, final long size, final long time, final TriggeringPolicy triggeringPolicy,
-            final RolloverStrategy rolloverStrategy, final String advertiseURI,
-            final Layout<? extends Serializable> layout, final boolean writeHeader, final ByteBuffer buffer) {
-        super(fileName, os, append, false, advertiseURI, layout, writeHeader, buffer);
-        this.size = size;
-        this.initialTime = time;
-        this.triggeringPolicy = triggeringPolicy;
-        this.rolloverStrategy = rolloverStrategy;
-        this.patternProcessor = new PatternProcessor(pattern);
-        this.patternProcessor.setPrevFileTime(time);
-    }
-
-    /**
-     * @since 2.7
-     */
-    protected RollingFileManager(final LoggerContext loggerContext, final String fileName, final String pattern, final OutputStream os,
-            final boolean append, final boolean createOnDemand, final long size, final long time,
-            final TriggeringPolicy triggeringPolicy, final RolloverStrategy rolloverStrategy,
-            final String advertiseURI, final Layout<? extends Serializable> layout, final boolean writeHeader, final ByteBuffer buffer) {
-        super(loggerContext, fileName, os, append, false, createOnDemand, advertiseURI, layout, writeHeader, buffer);
-        this.size = size;
-        this.initialTime = time;
-        this.triggeringPolicy = triggeringPolicy;
-        this.rolloverStrategy = rolloverStrategy;
-        this.patternProcessor = new PatternProcessor(pattern);
-        this.patternProcessor.setPrevFileTime(time);
-    }
-
-    public void initialize() {
-        triggeringPolicy.initialize(this);
-    }
-
-    /**
-     * Returns a RollingFileManager.
-     * @param fileName The file name.
-     * @param pattern The pattern for rolling file.
-     * @param append true if the file should be appended to.
-     * @param bufferedIO true if data should be buffered.
-     * @param policy The TriggeringPolicy.
-     * @param strategy The RolloverStrategy.
-     * @param advertiseURI the URI to use when advertising the file
-     * @param layout The Layout.
-     * @param bufferSize buffer size to use if bufferedIO is true
-     * @param immediateFlush flush on every write or not
-     * @param createOnDemand true if you want to lazy-create the file (a.k.a. on-demand.)
-     * @param configuration The configuration.
-     * @return A RollingFileManager.
-     */
-    public static RollingFileManager getFileManager(final String fileName, final String pattern, final boolean append,
-            final boolean bufferedIO, final TriggeringPolicy policy, final RolloverStrategy strategy,
-            final String advertiseURI, final Layout<? extends Serializable> layout, final int bufferSize,
-            final boolean immediateFlush, final boolean createOnDemand, final Configuration configuration) {
-
-        return (RollingFileManager) getManager(fileName, new FactoryData(pattern, append,
-            bufferedIO, policy, strategy, advertiseURI, layout, bufferSize, immediateFlush, createOnDemand, configuration), factory);
-    }
-
-    // override to make visible for unit tests
-    @Override
-    protected synchronized void write(final byte[] bytes, final int offset, final int length,
-            final boolean immediateFlush) {
-        super.write(bytes, offset, length, immediateFlush);
-    }
-
-    @Override
-    protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
-        size += length;
-        super.writeToDestination(bytes, offset, length);
-    }
-
-    public boolean isRenameEmptyFiles() {
-        return renameEmptyFiles;
-    }
-
-    public void setRenameEmptyFiles(final boolean renameEmptyFiles) {
-        this.renameEmptyFiles = renameEmptyFiles;
-    }
-
-    /**
-     * Returns the current size of the file.
-     * @return The size of the file in bytes.
-     */
-    public long getFileSize() {
-        return size + byteBuffer.position();
-    }
-
-    /**
-     * Returns the time the file was created.
-     * @return The time the file was created.
-     */
-    public long getFileTime() {
-        return initialTime;
-    }
-
-    /**
-     * Determines if a rollover should occur.
-     * @param event The LogEvent.
-     */
-    public synchronized void checkRollover(final LogEvent event) {
-        if (triggeringPolicy.isTriggeringEvent(event)) {
-            rollover();
-        }
-    }
-
-    @Override
-    public void releaseSub(final long timeout, final TimeUnit timeUnit) {
-        triggeringPolicy.stop(timeout, timeUnit);
-        super.releaseSub(timeout, timeUnit);
-    }
-    
-    public synchronized void rollover() {
-        if (rollover(rolloverStrategy)) {
-            try {
-                size = 0;
-                initialTime = System.currentTimeMillis();
-                createFileAfterRollover();
-            } catch (final IOException e) {
-                logError("Failed to create file after rollover", e);
-            }
-        }
-    }
-
-    protected void createFileAfterRollover() throws IOException  {
-        setOutputStream(new FileOutputStream(getFileName(), isAppend()));
-    }
-
-    /**
-     * Returns the pattern processor.
-     * @return The PatternProcessor.
-     */
-    public PatternProcessor getPatternProcessor() {
-        return patternProcessor;
-    }
-
-    public void setTriggeringPolicy(final TriggeringPolicy triggeringPolicy) {
-        triggeringPolicy.initialize(this);
-        triggeringPolicyUpdater.compareAndSet(this, this.triggeringPolicy, triggeringPolicy);
-    }
-
-    public void setRolloverStrategy(final RolloverStrategy rolloverStrategy) {
-        rolloverStrategyUpdater.compareAndSet(this, this.rolloverStrategy, rolloverStrategy);
-    }
-
-    /**
-     * Returns the triggering policy.
-     * @param <T> TriggeringPolicy type
-     * @return The TriggeringPolicy
-     */
-    @SuppressWarnings("unchecked")
-    public <T extends TriggeringPolicy> T getTriggeringPolicy() {
-        // TODO We could parameterize this class with a TriggeringPolicy instead of type casting here.
-        return (T) this.triggeringPolicy;
-    }
-
-    /**
-     * Returns the rollover strategy.
-     * @return The RolloverStrategy
-     */
-    public RolloverStrategy getRolloverStrategy() {
-        return this.rolloverStrategy;
-    }
-
-    private boolean rollover(final RolloverStrategy strategy) {
-
-        try {
-            // Block until the asynchronous operation is completed.
-            semaphore.acquire();
-        } catch (final InterruptedException e) {
-            logError("Thread interrupted while attempting to check rollover", e);
-            return false;
-        }
-
-        boolean success = false;
-        Future<?> future = null;
-
-        try {
-            final RolloverDescription descriptor = strategy.rollover(this);
-            if (descriptor != null) {
-                writeFooter();
-                closeOutputStream();
-                if (descriptor.getSynchronous() != null) {
-                    LOGGER.debug("RollingFileManager executing synchronous {}", descriptor.getSynchronous());
-                    try {
-                        success = descriptor.getSynchronous().execute();
-                    } catch (final Exception ex) {
-                        logError("Caught error in synchronous task", ex);
-                    }
-                }
-
-                if (success && descriptor.getAsynchronous() != null) {
-                    LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous());
-                    future = LoggerContext.getContext(false).submit(new AsyncAction(descriptor.getAsynchronous(), this));
-                }
-                return true;
-            }
-            return false;
-        } finally {
-            if (future == null || future.isDone() || future.isCancelled()) {
-                semaphore.release();
-            }
-        }
-
-    }
-
-    /**
-     * Performs actions asynchronously.
-     */
-    private static class AsyncAction extends AbstractAction {
-
-        private final Action action;
-        private final RollingFileManager manager;
-
-        /**
-         * Constructor.
-         * @param act The action to perform.
-         * @param manager The manager.
-         */
-        public AsyncAction(final Action act, final RollingFileManager manager) {
-            this.action = act;
-            this.manager = manager;
-        }
-
-        /**
-         * Executes an action.
-         *
-         * @return true if action was successful.  A return value of false will cause
-         *         the rollover to be aborted if possible.
-         * @throws java.io.IOException if IO error, a thrown exception will cause the rollover
-         *                             to be aborted if possible.
-         */
-        @Override
-        public boolean execute() throws IOException {
-            try {
-                return action.execute();
-            } finally {
-                manager.semaphore.release();
-            }
-        }
-
-        /**
-         * Cancels the action if not already initialized or waits till completion.
-         */
-        @Override
-        public void close() {
-            action.close();
-        }
-
-        /**
-         * Determines if action has been completed.
-         *
-         * @return true if action is complete.
-         */
-        @Override
-        public boolean isComplete() {
-            return action.isComplete();
-        }
-
-        @Override
-        public String toString() {
-            final StringBuilder builder = new StringBuilder();
-            builder.append(super.toString());
-            builder.append("[action=");
-            builder.append(action);
-            builder.append(", manager=");
-            builder.append(manager);
-            builder.append(", isComplete()=");
-            builder.append(isComplete());
-            builder.append(", isInterrupted()=");
-            builder.append(isInterrupted());
-            builder.append("]");
-            return builder.toString();
-        }
-    }
-
-    /**
-     * Factory data.
-     */
-    private static class FactoryData extends ConfigurationFactoryData {
-        private final String pattern;
-        private final boolean append;
-        private final boolean bufferedIO;
-        private final int bufferSize;
-        private final boolean immediateFlush;
-        private final boolean createOnDemand;
-        private final TriggeringPolicy policy;
-        private final RolloverStrategy strategy;
-        private final String advertiseURI;
-        private final Layout<? extends Serializable> layout;
-
-        /**
-         * Creates the data for the factory.
-         * @param pattern The pattern.
-         * @param append The append flag.
-         * @param bufferedIO The bufferedIO flag.
-         * @param advertiseURI
-         * @param layout The Layout.
-         * @param bufferSize the buffer size
-         * @param immediateFlush flush on every write or not
-         * @param createOnDemand true if you want to lazy-create the file (a.k.a. on-demand.)
-         * @param configuration The configuration
-         */
-        public FactoryData(final String pattern, final boolean append, final boolean bufferedIO,
-                final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
-                final Layout<? extends Serializable> layout, final int bufferSize, final boolean immediateFlush, 
-                final boolean createOnDemand, final Configuration configuration) {
-            super(configuration);
-            this.pattern = pattern;
-            this.append = append;
-            this.bufferedIO = bufferedIO;
-            this.bufferSize = bufferSize;
-            this.policy = policy;
-            this.strategy = strategy;
-            this.advertiseURI = advertiseURI;
-            this.layout = layout;
-            this.immediateFlush = immediateFlush;
-            this.createOnDemand = createOnDemand;
-        }
-
-        public TriggeringPolicy getTriggeringPolicy()
-        {
-            return this.policy;
-        }
-
-        public RolloverStrategy getRolloverStrategy()
-        {
-            return this.strategy;
-        }
-
-        @Override
-        public String toString() {
-            final StringBuilder builder = new StringBuilder();
-            builder.append(super.toString());
-            builder.append("[pattern=");
-            builder.append(pattern);
-            builder.append(", append=");
-            builder.append(append);
-            builder.append(", bufferedIO=");
-            builder.append(bufferedIO);
-            builder.append(", bufferSize=");
-            builder.append(bufferSize);
-            builder.append(", policy=");
-            builder.append(policy);
-            builder.append(", strategy=");
-            builder.append(strategy);
-            builder.append(", advertiseURI=");
-            builder.append(advertiseURI);
-            builder.append(", layout=");
-            builder.append(layout);
-            builder.append("]");
-            return builder.toString();
-        }
-    }
-
-    @Override
-    public void updateData(final Object data)
-    {
-        final FactoryData factoryData = (FactoryData) data;
-        setRolloverStrategy(factoryData.getRolloverStrategy());
-        setTriggeringPolicy(factoryData.getTriggeringPolicy());
-    }
-
-    /**
-     * Factory to create a RollingFileManager.
-     */
-    private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> {
-
-        /**
-         * Creates a RollingFileManager.
-         * @param name The name of the entity to manage.
-         * @param data The data required to create the entity.
-         * @return a RollingFileManager.
-         */
-        @Override
-        public RollingFileManager 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();
-            }
-            // LOG4J2-1140: check writeHeader before creating the file
-            final boolean writeHeader = !data.append || !file.exists();
-            try {
-                final boolean created = data.createOnDemand ? false : file.createNewFile();
-                LOGGER.trace("New file '{}' created = {}", name, created);
-            } catch (final IOException ioe) {
-                LOGGER.error("Unable to create file " + name, ioe);
-                return null;
-            }
-            final long size = data.append ? file.length() : 0;
-
-            try {
-                final int actualSize = data.bufferedIO ? data.bufferSize : Constants.ENCODER_BYTE_BUFFER_SIZE;
-                final ByteBuffer buffer = ByteBuffer.wrap(new byte[actualSize]);
-                final OutputStream os = data.createOnDemand ? null : new FileOutputStream(name, data.append);
-                final long time = data.createOnDemand? System.currentTimeMillis() : file.lastModified(); // LOG4J2-531 create file first so time has valid value
-                
-                return new RollingFileManager(data.getLoggerContext(), name, data.pattern, os,
-                        data.append, data.createOnDemand, size, time, data.policy, data.strategy, data.advertiseURI,
-                        data.layout, writeHeader, buffer);
-            } catch (final IOException ex) {
-                LOGGER.error("RollingFileManager (" + 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.rolling;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConfigurationFactoryData;
+import org.apache.logging.log4j.core.appender.FileManager;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction;
+import org.apache.logging.log4j.core.appender.rolling.action.Action;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.util.Constants;
+
+/**
+ * The Rolling File Manager.
+ */
+public class RollingFileManager extends FileManager {
+
+    private static RollingFileManagerFactory factory = new RollingFileManagerFactory();
+
+    protected long size;
+    private long initialTime;
+    private final PatternProcessor patternProcessor;
+    private final Semaphore semaphore = new Semaphore(1);
+    private volatile TriggeringPolicy triggeringPolicy;
+    private volatile RolloverStrategy rolloverStrategy;
+    private volatile boolean renameEmptyFiles = false;
+
+    private static final AtomicReferenceFieldUpdater<RollingFileManager, TriggeringPolicy> triggeringPolicyUpdater =
+            AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, TriggeringPolicy.class, "triggeringPolicy");
+
+    private static final AtomicReferenceFieldUpdater<RollingFileManager, RolloverStrategy> rolloverStrategyUpdater =
+            AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, RolloverStrategy.class, "rolloverStrategy");
+
+    @Deprecated
+    protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
+            final boolean append, final long size, final long time, final TriggeringPolicy triggeringPolicy,
+            final RolloverStrategy rolloverStrategy, final String advertiseURI,
+            final Layout<? extends Serializable> layout, final int bufferSize, final boolean writeHeader) {
+        this(fileName, pattern, os, append, size, time, triggeringPolicy, rolloverStrategy, advertiseURI, layout,
+                writeHeader, ByteBuffer.wrap(new byte[Constants.ENCODER_BYTE_BUFFER_SIZE]));
+    }
+
+    @Deprecated
+    protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
+            final boolean append, final long size, final long time, final TriggeringPolicy triggeringPolicy,
+            final RolloverStrategy rolloverStrategy, final String advertiseURI,
+            final Layout<? extends Serializable> layout, final boolean writeHeader, final ByteBuffer buffer) {
+        super(fileName, os, append, false, advertiseURI, layout, writeHeader, buffer);
+        this.size = size;
+        this.initialTime = time;
+        this.triggeringPolicy = triggeringPolicy;
+        this.rolloverStrategy = rolloverStrategy;
+        this.patternProcessor = new PatternProcessor(pattern);
+        this.patternProcessor.setPrevFileTime(time);
+    }
+
+    /**
+     * @since 2.7
+     */
+    protected RollingFileManager(final LoggerContext loggerContext, final String fileName, final String pattern, final OutputStream os,
+            final boolean append, final boolean createOnDemand, final long size, final long time,
+            final TriggeringPolicy triggeringPolicy, final RolloverStrategy rolloverStrategy,
+            final String advertiseURI, final Layout<? extends Serializable> layout, final boolean writeHeader, final ByteBuffer buffer) {
+        super(loggerContext, fileName, os, append, false, createOnDemand, advertiseURI, layout, writeHeader, buffer);
+        this.size = size;
+        this.initialTime = time;
+        this.triggeringPolicy = triggeringPolicy;
+        this.rolloverStrategy = rolloverStrategy;
+        this.patternProcessor = new PatternProcessor(pattern);
+        this.patternProcessor.setPrevFileTime(time);
+    }
+
+    public void initialize() {
+        triggeringPolicy.initialize(this);
+    }
+
+    /**
+     * Returns a RollingFileManager.
+     * @param fileName The file name.
+     * @param pattern The pattern for rolling file.
+     * @param append true if the file should be appended to.
+     * @param bufferedIO true if data should be buffered.
+     * @param policy The TriggeringPolicy.
+     * @param strategy The RolloverStrategy.
+     * @param advertiseURI the URI to use when advertising the file
+     * @param layout The Layout.
+     * @param bufferSize buffer size to use if bufferedIO is true
+     * @param immediateFlush flush on every write or not
+     * @param createOnDemand true if you want to lazy-create the file (a.k.a. on-demand.)
+     * @param configuration The configuration.
+     * @return A RollingFileManager.
+     */
+    public static RollingFileManager getFileManager(final String fileName, final String pattern, final boolean append,
+            final boolean bufferedIO, final TriggeringPolicy policy, final RolloverStrategy strategy,
+            final String advertiseURI, final Layout<? extends Serializable> layout, final int bufferSize,
+            final boolean immediateFlush, final boolean createOnDemand, final Configuration configuration) {
+
+        return (RollingFileManager) getManager(fileName, new FactoryData(pattern, append,
+            bufferedIO, policy, strategy, advertiseURI, layout, bufferSize, immediateFlush, createOnDemand, configuration), factory);
+    }
+
+    // override to make visible for unit tests
+    @Override
+    protected synchronized void write(final byte[] bytes, final int offset, final int length,
+            final boolean immediateFlush) {
+        super.write(bytes, offset, length, immediateFlush);
+    }
+
+    @Override
+    protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
+        size += length;
+        super.writeToDestination(bytes, offset, length);
+    }
+
+    public boolean isRenameEmptyFiles() {
+        return renameEmptyFiles;
+    }
+
+    public void setRenameEmptyFiles(final boolean renameEmptyFiles) {
+        this.renameEmptyFiles = renameEmptyFiles;
+    }
+
+    /**
+     * Returns the current size of the file.
+     * @return The size of the file in bytes.
+     */
+    public long getFileSize() {
+        return size + byteBuffer.position();
+    }
+
+    /**
+     * Returns the time the file was created.
+     * @return The time the file was created.
+     */
+    public long getFileTime() {
+        return initialTime;
+    }
+
+    /**
+     * Determines if a rollover should occur.
+     * @param event The LogEvent.
+     */
+    public synchronized void checkRollover(final LogEvent event) {
+        if (triggeringPolicy.isTriggeringEvent(event)) {
+            rollover();
+        }
+    }
+
+    @Override
+    public boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
+        boolean stopped = triggeringPolicy.stop(timeout, timeUnit);
+        return stopped && super.releaseSub(timeout, timeUnit);
+    }
+    
+    public synchronized void rollover() {
+        if (rollover(rolloverStrategy)) {
+            try {
+                size = 0;
+                initialTime = System.currentTimeMillis();
+                createFileAfterRollover();
+            } catch (final IOException e) {
+                logError("Failed to create file after rollover", e);
+            }
+        }
+    }
+
+    protected void createFileAfterRollover() throws IOException  {
+        setOutputStream(new FileOutputStream(getFileName(), isAppend()));
+    }
+
+    /**
+     * Returns the pattern processor.
+     * @return The PatternProcessor.
+     */
+    public PatternProcessor getPatternProcessor() {
+        return patternProcessor;
+    }
+
+    public void setTriggeringPolicy(final TriggeringPolicy triggeringPolicy) {
+        triggeringPolicy.initialize(this);
+        triggeringPolicyUpdater.compareAndSet(this, this.triggeringPolicy, triggeringPolicy);
+    }
+
+    public void setRolloverStrategy(final RolloverStrategy rolloverStrategy) {
+        rolloverStrategyUpdater.compareAndSet(this, this.rolloverStrategy, rolloverStrategy);
+    }
+
+    /**
+     * Returns the triggering policy.
+     * @param <T> TriggeringPolicy type
+     * @return The TriggeringPolicy
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends TriggeringPolicy> T getTriggeringPolicy() {
+        // TODO We could parameterize this class with a TriggeringPolicy instead of type casting here.
+        return (T) this.triggeringPolicy;
+    }
+
+    /**
+     * Returns the rollover strategy.
+     * @return The RolloverStrategy
+     */
+    public RolloverStrategy getRolloverStrategy() {
+        return this.rolloverStrategy;
+    }
+
+    private boolean rollover(final RolloverStrategy strategy) {
+
+        try {
+            // Block until the asynchronous operation is completed.
+            semaphore.acquire();
+        } catch (final InterruptedException e) {
+            logError("Thread interrupted while attempting to check rollover", e);
+            return false;
+        }
+
+        boolean success = false;
+        Future<?> future = null;
+
+        try {
+            final RolloverDescription descriptor = strategy.rollover(this);
+            if (descriptor != null) {
+                writeFooter();
+                closeOutputStream();
+                if (descriptor.getSynchronous() != null) {
+                    LOGGER.debug("RollingFileManager executing synchronous {}", descriptor.getSynchronous());
+                    try {
+                        success = descriptor.getSynchronous().execute();
+                    } catch (final Exception ex) {
+                        logError("Caught error in synchronous task", ex);
+                    }
+                }
+
+                if (success && descriptor.getAsynchronous() != null) {
+                    LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous());
+                    future = LoggerContext.getContext(false).submit(new AsyncAction(descriptor.getAsynchronous(), this));
+                }
+                return true;
+            }
+            return false;
+        } finally {
+            if (future == null || future.isDone() || future.isCancelled()) {
+                semaphore.release();
+            }
+        }
+
+    }
+
+    /**
+     * Performs actions asynchronously.
+     */
+    private static class AsyncAction extends AbstractAction {
+
+        private final Action action;
+        private final RollingFileManager manager;
+
+        /**
+         * Constructor.
+         * @param act The action to perform.
+         * @param manager The manager.
+         */
+        public AsyncAction(final Action act, final RollingFileManager manager) {
+            this.action = act;
+            this.manager = manager;
+        }
+
+        /**
+         * Executes an action.
+         *
+         * @return true if action was successful.  A return value of false will cause
+         *         the rollover to be aborted if possible.
+         * @throws java.io.IOException if IO error, a thrown exception will cause the rollover
+         *                             to be aborted if possible.
+         */
+        @Override
+        public boolean execute() throws IOException {
+            try {
+                return action.execute();
+            } finally {
+                manager.semaphore.release();
+            }
+        }
+
+        /**
+         * Cancels the action if not already initialized or waits till completion.
+         */
+        @Override
+        public void close() {
+            action.close();
+        }
+
+        /**
+         * Determines if action has been completed.
+         *
+         * @return true if action is complete.
+         */
+        @Override
+        public boolean isComplete() {
+            return action.isComplete();
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append(super.toString());
+            builder.append("[action=");
+            builder.append(action);
+            builder.append(", manager=");
+            builder.append(manager);
+            builder.append(", isComplete()=");
+            builder.append(isComplete());
+            builder.append(", isInterrupted()=");
+            builder.append(isInterrupted());
+            builder.append("]");
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Factory data.
+     */
+    private static class FactoryData extends ConfigurationFactoryData {
+        private final String pattern;
+        private final boolean append;
+        private final boolean bufferedIO;
+        private final int bufferSize;
+        private final boolean immediateFlush;
+        private final boolean createOnDemand;
+        private final TriggeringPolicy policy;
+        private final RolloverStrategy strategy;
+        private final String advertiseURI;
+        private final Layout<? extends Serializable> layout;
+
+        /**
+         * Creates the data for the factory.
+         * @param pattern The pattern.
+         * @param append The append flag.
+         * @param bufferedIO The bufferedIO flag.
+         * @param advertiseURI
+         * @param layout The Layout.
+         * @param bufferSize the buffer size
+         * @param immediateFlush flush on every write or not
+         * @param createOnDemand true if you want to lazy-create the file (a.k.a. on-demand.)
+         * @param configuration The configuration
+         */
+        public FactoryData(final String pattern, final boolean append, final boolean bufferedIO,
+                final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
+                final Layout<? extends Serializable> layout, final int bufferSize, final boolean immediateFlush, 
+                final boolean createOnDemand, final Configuration configuration) {
+            super(configuration);
+            this.pattern = pattern;
+            this.append = append;
+            this.bufferedIO = bufferedIO;
+            this.bufferSize = bufferSize;
+            this.policy = policy;
+            this.strategy = strategy;
+            this.advertiseURI = advertiseURI;
+            this.layout = layout;
+            this.immediateFlush = immediateFlush;
+            this.createOnDemand = createOnDemand;
+        }
+
+        public TriggeringPolicy getTriggeringPolicy()
+        {
+            return this.policy;
+        }
+
+        public RolloverStrategy getRolloverStrategy()
+        {
+            return this.strategy;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append(super.toString());
+            builder.append("[pattern=");
+            builder.append(pattern);
+            builder.append(", append=");
+            builder.append(append);
+            builder.append(", bufferedIO=");
+            builder.append(bufferedIO);
+            builder.append(", bufferSize=");
+            builder.append(bufferSize);
+            builder.append(", policy=");
+            builder.append(policy);
+            builder.append(", strategy=");
+            builder.append(strategy);
+            builder.append(", advertiseURI=");
+            builder.append(advertiseURI);
+            builder.append(", layout=");
+            builder.append(layout);
+            builder.append("]");
+            return builder.toString();
+        }
+    }
+
+    @Override
+    public void updateData(final Object data)
+    {
+        final FactoryData factoryData = (FactoryData) data;
+        setRolloverStrategy(factoryData.getRolloverStrategy());
+        setTriggeringPolicy(factoryData.getTriggeringPolicy());
+    }
+
+    /**
+     * Factory to create a RollingFileManager.
+     */
+    private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> {
+
+        /**
+         * Creates a RollingFileManager.
+         * @param name The name of the entity to manage.
+         * @param data The data required to create the entity.
+         * @return a RollingFileManager.
+         */
+        @Override
+        public RollingFileManager 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();
+            }
+            // LOG4J2-1140: check writeHeader before creating the file
+            final boolean writeHeader = !data.append || !file.exists();
+            try {
+                final boolean created = data.createOnDemand ? false : file.createNewFile();
+                LOGGER.trace("New file '{}' created = {}", name, created);
+            } catch (final IOException ioe) {
+                LOGGER.error("Unable to create file " + name, ioe);
+                return null;
+            }
+            final long size = data.append ? file.length() : 0;
+
+            try {
+                final int actualSize = data.bufferedIO ? data.bufferSize : Constants.ENCODER_BYTE_BUFFER_SIZE;
+                final ByteBuffer buffer = ByteBuffer.wrap(new byte[actualSize]);
+                final OutputStream os = data.createOnDemand ? null : new FileOutputStream(name, data.append);
+                final long time = data.createOnDemand? System.currentTimeMillis() : file.lastModified(); // LOG4J2-531 create file first so time has valid value
+                
+                return new RollingFileManager(data.getLoggerContext(), name, data.pattern, os,
+                        data.append, data.createOnDemand, size, time, data.policy, data.strategy, data.advertiseURI,
+                        data.layout, writeHeader, buffer);
+            } catch (final IOException ex) {
+                LOGGER.error("RollingFileManager (" + name + ") " + ex, ex);
+            }
+            return null;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/78fe32c0/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
index a52921d..abeb5ee 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
@@ -1,144 +1,144 @@
-/*
- * 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.net;
-
-import java.util.Properties;
-import java.util.concurrent.TimeUnit;
-
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-
-import org.apache.logging.log4j.core.appender.AbstractManager;
-import org.apache.logging.log4j.core.appender.ManagerFactory;
-import org.apache.logging.log4j.core.util.JndiCloser;
-
-/**
- * JNDI {@link javax.naming.Context} manager.
- *
- * @since 2.1
- */
-public class JndiManager extends AbstractManager {
-
-    private static final JndiManagerFactory FACTORY = new JndiManagerFactory();
-
-    private final Context context;
-
-    private JndiManager(final String name, final Context context) {
-        super(null, name);
-        this.context = context;
-    }
-
-    /**
-     * Gets the default JndiManager using the default {@link javax.naming.InitialContext}.
-     *
-     * @return the default JndiManager
-     */
-    public static JndiManager getDefaultManager() {
-        return getManager(JndiManager.class.getName(), FACTORY, null);
-    }
-
-    /**
-     * Gets a named JndiManager using the default {@link javax.naming.InitialContext}.
-     * @param name the name of the JndiManager instance to create or use if available
-     * @return a default JndiManager
-     */
-    public static JndiManager getDefaultManager(final String name) {
-        return getManager(name, FACTORY, null);
-    }
-
-    /**
-     * Gets a JndiManager with the provided configuration information.
-     *
-     * @param initialContextFactoryName Fully qualified class name of an implementation of
-     *                                  {@link javax.naming.spi.InitialContextFactory}.
-     * @param providerURL               The provider URL to use for the JNDI connection (specific to the above factory).
-     * @param urlPkgPrefixes            A colon-separated list of package prefixes for the class name of the factory
-     *                                  class that will create a URL context factory
-     * @param securityPrincipal         The name of the identity of the Principal.
-     * @param securityCredentials       The security credentials of the Principal.
-     * @param additionalProperties      Any additional JNDI environment properties to set or {@code null} for none.
-     * @return the JndiManager for the provided parameters.
-     */
-    public static JndiManager getJndiManager(final String initialContextFactoryName,
-                                             final String providerURL,
-                                             final String urlPkgPrefixes,
-                                             final String securityPrincipal,
-                                             final String securityCredentials,
-                                             final Properties additionalProperties) {
-        final String name = JndiManager.class.getName() + '@' + JndiManager.class.hashCode();
-        if (initialContextFactoryName == null) {
-            return getManager(name, FACTORY, null);
-        }
-        final Properties properties = new Properties();
-        properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName);
-        if (providerURL != null) {
-            properties.setProperty(Context.PROVIDER_URL, providerURL);
-        } else {
-            LOGGER.warn("The JNDI InitialContextFactory class name [{}] was provided, but there was no associated " +
-                "provider URL. This is likely to cause problems.", initialContextFactoryName);
-        }
-        if (urlPkgPrefixes != null) {
-            properties.setProperty(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
-        }
-        if (securityPrincipal != null) {
-            properties.setProperty(Context.SECURITY_PRINCIPAL, securityPrincipal);
-            if (securityCredentials != null) {
-                properties.setProperty(Context.SECURITY_CREDENTIALS, securityCredentials);
-            } else {
-                LOGGER.warn("A security principal [{}] was provided, but with no corresponding security credentials.",
-                    securityPrincipal);
-            }
-        }
-        if (additionalProperties != null) {
-            properties.putAll(additionalProperties);
-        }
-        return getManager(name, FACTORY, properties);
-    }
-
-    @Override
-    protected void releaseSub(final long timeout, final TimeUnit timeUnit) {
-        JndiCloser.closeSilently(this.context);
-    }
-
-    /**
-     * Looks up a named object through this JNDI context.
-     *
-     * @param name name of the object to look up.
-     * @param <T>  the type of the object.
-     * @return the named object if it could be located.
-     * @throws NamingException
-     */
-    @SuppressWarnings("unchecked")
-    public <T> T lookup(final String name) throws NamingException {
-        return (T) this.context.lookup(name);
-    }
-
-    private static class JndiManagerFactory implements ManagerFactory<JndiManager, Properties> {
-
-        @Override
-        public JndiManager createManager(final String name, final Properties data) {
-            try {
-                return new JndiManager(name, new InitialContext(data));
-            } catch (final NamingException e) {
-                LOGGER.error("Error creating JNDI InitialContext.", e);
-                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.net;
+
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.util.JndiCloser;
+
+/**
+ * JNDI {@link javax.naming.Context} manager.
+ *
+ * @since 2.1
+ */
+public class JndiManager extends AbstractManager {
+
+    private static final JndiManagerFactory FACTORY = new JndiManagerFactory();
+
+    private final Context context;
+
+    private JndiManager(final String name, final Context context) {
+        super(null, name);
+        this.context = context;
+    }
+
+    /**
+     * Gets the default JndiManager using the default {@link javax.naming.InitialContext}.
+     *
+     * @return the default JndiManager
+     */
+    public static JndiManager getDefaultManager() {
+        return getManager(JndiManager.class.getName(), FACTORY, null);
+    }
+
+    /**
+     * Gets a named JndiManager using the default {@link javax.naming.InitialContext}.
+     * @param name the name of the JndiManager instance to create or use if available
+     * @return a default JndiManager
+     */
+    public static JndiManager getDefaultManager(final String name) {
+        return getManager(name, FACTORY, null);
+    }
+
+    /**
+     * Gets a JndiManager with the provided configuration information.
+     *
+     * @param initialContextFactoryName Fully qualified class name of an implementation of
+     *                                  {@link javax.naming.spi.InitialContextFactory}.
+     * @param providerURL               The provider URL to use for the JNDI connection (specific to the above factory).
+     * @param urlPkgPrefixes            A colon-separated list of package prefixes for the class name of the factory
+     *                                  class that will create a URL context factory
+     * @param securityPrincipal         The name of the identity of the Principal.
+     * @param securityCredentials       The security credentials of the Principal.
+     * @param additionalProperties      Any additional JNDI environment properties to set or {@code null} for none.
+     * @return the JndiManager for the provided parameters.
+     */
+    public static JndiManager getJndiManager(final String initialContextFactoryName,
+                                             final String providerURL,
+                                             final String urlPkgPrefixes,
+                                             final String securityPrincipal,
+                                             final String securityCredentials,
+                                             final Properties additionalProperties) {
+        final String name = JndiManager.class.getName() + '@' + JndiManager.class.hashCode();
+        if (initialContextFactoryName == null) {
+            return getManager(name, FACTORY, null);
+        }
+        final Properties properties = new Properties();
+        properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName);
+        if (providerURL != null) {
+            properties.setProperty(Context.PROVIDER_URL, providerURL);
+        } else {
+            LOGGER.warn("The JNDI InitialContextFactory class name [{}] was provided, but there was no associated " +
+                "provider URL. This is likely to cause problems.", initialContextFactoryName);
+        }
+        if (urlPkgPrefixes != null) {
+            properties.setProperty(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+        }
+        if (securityPrincipal != null) {
+            properties.setProperty(Context.SECURITY_PRINCIPAL, securityPrincipal);
+            if (securityCredentials != null) {
+                properties.setProperty(Context.SECURITY_CREDENTIALS, securityCredentials);
+            } else {
+                LOGGER.warn("A security principal [{}] was provided, but with no corresponding security credentials.",
+                    securityPrincipal);
+            }
+        }
+        if (additionalProperties != null) {
+            properties.putAll(additionalProperties);
+        }
+        return getManager(name, FACTORY, properties);
+    }
+
+    @Override
+    protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
+        return JndiCloser.closeSilently(this.context);
+    }
+
+    /**
+     * Looks up a named object through this JNDI context.
+     *
+     * @param name name of the object to look up.
+     * @param <T>  the type of the object.
+     * @return the named object if it could be located.
+     * @throws NamingException
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T lookup(final String name) throws NamingException {
+        return (T) this.context.lookup(name);
+    }
+
+    private static class JndiManagerFactory implements ManagerFactory<JndiManager, Properties> {
+
+        @Override
+        public JndiManager createManager(final String name, final Properties data) {
+            try {
+                return new JndiManager(name, new InitialContext(data));
+            } catch (final NamingException e) {
+                LOGGER.error("Error creating JNDI InitialContext.", e);
+                return null;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/78fe32c0/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/JmsServer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/JmsServer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/JmsServer.java
index 15e0a3f..730e778 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/JmsServer.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/JmsServer.java
@@ -1,147 +1,148 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.net.server;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.charset.Charset;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import javax.jms.JMSException;
-import javax.jms.Message;
-import javax.jms.MessageConsumer;
-import javax.jms.MessageListener;
-import javax.jms.ObjectMessage;
-
-import org.apache.logging.log4j.LoggingException;
-import org.apache.logging.log4j.core.AbstractLifeCycle;
-import org.apache.logging.log4j.core.LifeCycle;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.LogEventListener;
-import org.apache.logging.log4j.core.appender.mom.JmsManager;
-import org.apache.logging.log4j.core.net.JndiManager;
-
-/**
- * LogEventListener server that receives LogEvents over a JMS {@link javax.jms.Destination}.
- *
- * @since 2.1
- */
-public class JmsServer extends LogEventListener implements MessageListener, LifeCycle {
-
-    private final AtomicReference<State> state = new AtomicReference<>(State.INITIALIZED);
-    private final JmsManager jmsManager;
-    private MessageConsumer messageConsumer;
-
-    public JmsServer(final String connectionFactoryBindingName,
-                     final String destinationBindingName,
-                     final String username,
-                     final String password) {
-        final String managerName = JmsServer.class.getName() + '@' + JmsServer.class.hashCode();
-        final JndiManager jndiManager = JndiManager.getDefaultManager(managerName);
-        jmsManager = JmsManager.getJmsManager(managerName, jndiManager, connectionFactoryBindingName,
-            destinationBindingName, username, password);
-    }
-
-    @Override
-    public State getState() {
-        return state.get();
-    }
-
-    @Override
-    public void onMessage(final Message message) {
-        try {
-            if (message instanceof ObjectMessage) {
-                final Object body = ((ObjectMessage) message).getObject();
-                if (body instanceof LogEvent) {
-                    log((LogEvent) body);
-                } else {
-                    LOGGER.warn("Expected ObjectMessage to contain LogEvent. Got type {} instead.", body.getClass());
-                }
-            } else {
-                LOGGER.warn("Received message of type {} and JMSType {} which cannot be handled.", message.getClass(),
-                    message.getJMSType());
-            }
-        } catch (final JMSException e) {
-            LOGGER.catching(e);
-        }
-    }
-
-    @Override
-    public void initialize() {
-    }
-
-    @Override
-    public void start() {
-        if (state.compareAndSet(State.INITIALIZED, State.STARTING)) {
-            try {
-                messageConsumer = jmsManager.createMessageConsumer();
-                messageConsumer.setMessageListener(this);
-            } catch (final JMSException e) {
-                throw new LoggingException(e);
-            }
-        }
-    }
-
-    @Override    
-    public void stop() {
-        stop(AbstractLifeCycle.DEFAULT_STOP_TIMEOUT, AbstractLifeCycle.DEFAULT_STOP_TIMEUNIT);
-    }
-    
-    @Override
-    public boolean stop(final long timeout, final TimeUnit timeUnit) {
-        try {
-            messageConsumer.close();
-        } catch (final JMSException e) {
-            LOGGER.debug("Exception closing {}", messageConsumer, e);
-        }
-        jmsManager.stop(timeout, timeUnit);
-        return true;
-    }
-
-    @Override
-    public boolean isStarted() {
-        return state.get() == State.STARTED;
-    }
-
-    @Override
-    public boolean isStopped() {
-        return state.get() == State.STOPPED;
-    }
-
-    /**
-     * Starts and runs this server until the user types "exit" into standard input.
-     *
-     * @throws IOException
-     * @since 2.6
-     */
-    public void run() throws IOException {
-        this.start();
-        System.out.println("Type \"exit\" to quit.");
-        final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, Charset.defaultCharset()));
-        while (true) {
-            final String line = stdin.readLine();
-            if (line == null || line.equalsIgnoreCase("exit")) {
-                System.out.println("Exiting. Kill the application if it does not exit due to daemon threads.");
-                this.stop();
-                return;
-            }
-        }
-    }
-
-}
+/*
+ * 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.net.server;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.ObjectMessage;
+
+import org.apache.logging.log4j.LoggingException;
+import org.apache.logging.log4j.core.AbstractLifeCycle;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+import org.apache.logging.log4j.core.appender.mom.JmsManager;
+import org.apache.logging.log4j.core.net.JndiManager;
+
+/**
+ * LogEventListener server that receives LogEvents over a JMS {@link javax.jms.Destination}.
+ *
+ * @since 2.1
+ */
+public class JmsServer extends LogEventListener implements MessageListener, LifeCycle {
+
+    private final AtomicReference<State> state = new AtomicReference<>(State.INITIALIZED);
+    private final JmsManager jmsManager;
+    private MessageConsumer messageConsumer;
+
+    public JmsServer(final String connectionFactoryBindingName,
+                     final String destinationBindingName,
+                     final String username,
+                     final String password) {
+        final String managerName = JmsServer.class.getName() + '@' + JmsServer.class.hashCode();
+        final JndiManager jndiManager = JndiManager.getDefaultManager(managerName);
+        jmsManager = JmsManager.getJmsManager(managerName, jndiManager, connectionFactoryBindingName,
+            destinationBindingName, username, password);
+    }
+
+    @Override
+    public State getState() {
+        return state.get();
+    }
+
+    @Override
+    public void onMessage(final Message message) {
+        try {
+            if (message instanceof ObjectMessage) {
+                final Object body = ((ObjectMessage) message).getObject();
+                if (body instanceof LogEvent) {
+                    log((LogEvent) body);
+                } else {
+                    LOGGER.warn("Expected ObjectMessage to contain LogEvent. Got type {} instead.", body.getClass());
+                }
+            } else {
+                LOGGER.warn("Received message of type {} and JMSType {} which cannot be handled.", message.getClass(),
+                    message.getJMSType());
+            }
+        } catch (final JMSException e) {
+            LOGGER.catching(e);
+        }
+    }
+
+    @Override
+    public void initialize() {
+    }
+
+    @Override
+    public void start() {
+        if (state.compareAndSet(State.INITIALIZED, State.STARTING)) {
+            try {
+                messageConsumer = jmsManager.createMessageConsumer();
+                messageConsumer.setMessageListener(this);
+            } catch (final JMSException e) {
+                throw new LoggingException(e);
+            }
+        }
+    }
+
+    @Override    
+    public void stop() {
+        stop(AbstractLifeCycle.DEFAULT_STOP_TIMEOUT, AbstractLifeCycle.DEFAULT_STOP_TIMEUNIT);
+    }
+    
+    @Override
+    public boolean stop(final long timeout, final TimeUnit timeUnit) {
+        boolean stopped = true;
+        try {
+            messageConsumer.close();
+        } catch (final JMSException e) {
+            LOGGER.debug("Exception closing {}", messageConsumer, e);
+            stopped = false;
+        }
+        return stopped && jmsManager.stop(timeout, timeUnit);
+    }
+
+    @Override
+    public boolean isStarted() {
+        return state.get() == State.STARTED;
+    }
+
+    @Override
+    public boolean isStopped() {
+        return state.get() == State.STOPPED;
+    }
+
+    /**
+     * Starts and runs this server until the user types "exit" into standard input.
+     *
+     * @throws IOException
+     * @since 2.6
+     */
+    public void run() throws IOException {
+        this.start();
+        System.out.println("Type \"exit\" to quit.");
+        final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, Charset.defaultCharset()));
+        while (true) {
+            final String line = stdin.readLine();
+            if (line == null || line.equalsIgnoreCase("exit")) {
+                System.out.println("Exiting. Kill the application if it does not exit due to daemon threads.");
+                this.stop();
+                return;
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/78fe32c0/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JndiCloser.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JndiCloser.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JndiCloser.java
index 671c922..2114b40 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JndiCloser.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JndiCloser.java
@@ -1,58 +1,60 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.util;
-
-import javax.naming.Context;
-import javax.naming.NamingException;
-
-/**
- * Helper class for closing JNDI resources.
- * 
- * This class is separate from {@link Closer} because JNDI is not in Android.
- */
-public final class JndiCloser {
-    
-    private JndiCloser() {
-    }
-
-    /**
-     * Closes the specified {@code Context}.
-     *
-     * @param context the JNDI Context to close, may be {@code null}
-     * @throws NamingException if a problem occurred closing the specified JNDI Context
-     */
-    public static void close(final Context context) throws NamingException {
-        if (context != null) {
-            context.close();
-        }
-    }
-
-    /**
-     * Closes the specified {@code Context}, ignoring any exceptions thrown by the close operation.
-     *
-     * @param context the JNDI Context to close, may be {@code null}
-     */
-    public static void closeSilently(final Context context) {
-        try {
-            close(context);
-        } catch (final NamingException ignored) {
-            // ignored
-        }
-    }
-
-}
+/*
+ * 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.util;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+/**
+ * Helper class for closing JNDI resources.
+ * 
+ * This class is separate from {@link Closer} because JNDI is not in Android.
+ */
+public final class JndiCloser {
+    
+    private JndiCloser() {
+    }
+
+    /**
+     * Closes the specified {@code Context}.
+     *
+     * @param context the JNDI Context to close, may be {@code null}
+     * @throws NamingException if a problem occurred closing the specified JNDI Context
+     */
+    public static void close(final Context context) throws NamingException {
+        if (context != null) {
+            context.close();
+        }
+    }
+
+    /**
+     * Closes the specified {@code Context}, ignoring any exceptions thrown by the close operation.
+     *
+     * @param context the JNDI Context to close, may be {@code null}
+     */
+    public static boolean closeSilently(final Context context) {
+        try {
+            close(context);
+            return true;
+        } catch (final NamingException ignored) {
+            // ignored
+            return false;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/78fe32c0/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java
----------------------------------------------------------------------
diff --git a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java
index 1df6bed..a29df92 100644
--- a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java
+++ b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java
@@ -109,10 +109,10 @@ public final class FlumeAppender extends AbstractAppender implements FlumeEventF
     @Override
     public boolean stop(final long timeout, final TimeUnit timeUnit) {
         setStopping();
-        super.stop(timeout, timeUnit, false);
-        manager.stop(timeout, timeUnit);
+        boolean stopped = super.stop(timeout, timeUnit, false);
+        stopped &= manager.stop(timeout, timeUnit);
         setStopped();
-        return true;
+        return stopped;
     }
 
     /**