Repository: logging-log4j2
Updated Branches:
  refs/heads/LOG4J2-89 a7977f5a1 -> 3c84ef945


LOG4J2-381 - Allow triggering policy and rollover strategy to be modified 
during reconfiguration


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

Branch: refs/heads/LOG4J2-89
Commit: 3c84ef9450cc57274d2f59767002fb0bb99b815a
Parents: a7977f5
Author: Ralph Goers <[email protected]>
Authored: Wed Nov 18 17:42:45 2015 -0700
Committer: Ralph Goers <[email protected]>
Committed: Wed Nov 18 17:42:45 2015 -0700

----------------------------------------------------------------------
 .../log4j/core/appender/AbstractManager.java    |   5 +
 .../core/appender/AbstractManager.java.orig     | 166 +++++++++
 .../appender/rolling/RollingFileManager.java    |  40 ++-
 .../rolling/RollingFileManager.java.orig        | 343 +++++++++++++++++++
 .../rolling/RollingRandomAccessFileManager.java |  17 +
 .../RollingRandomAccessFileManager.java.orig    | 252 ++++++++++++++
 src/changes/changes.xml                         |   6 +
 7 files changed, 827 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3c84ef94/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
index d256a4f..45f5141 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
@@ -76,6 +76,8 @@ public abstract class AbstractManager {
                             + name + "] with data [" + data + "]");
                 }
                 MAP.put(name, manager);
+            } else {
+                manager.updateData(data);
             }
             manager.count++;
             return manager;
@@ -84,6 +86,9 @@ public abstract class AbstractManager {
         }
     }
 
+    public void updateData(final Object data) {
+    }
+
     /**
      * Determines if a Manager with the specified name exists.
      * @param name The name of the Manager.

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3c84ef94/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java.orig
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java.orig
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java.orig
new file mode 100644
index 0000000..d256a4f
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java.orig
@@ -0,0 +1,166 @@
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Abstract base class used to register managers.
+ */
+public abstract class AbstractManager {
+
+    /**
+     * Allow subclasses access to the status logger without creating another 
instance.
+     */
+    protected static final Logger LOGGER = StatusLogger.getLogger();
+
+    // Need to lock that map instead of using a ConcurrentMap due to stop 
removing the
+    // manager from the map and closing the stream, requiring the whole stop 
method to be locked.
+    private static final Map<String, AbstractManager> MAP = new HashMap<>();
+
+    private static final Lock LOCK = new ReentrantLock();
+
+    /**
+     * Number of Appenders using this manager.
+     */
+    protected int count;
+
+    private final String name;
+
+    protected AbstractManager(final String name) {
+        this.name = name;
+        LOGGER.debug("Starting {} {}", this.getClass().getSimpleName(), name);
+    }
+
+    /**
+     * Retrieves a Manager if it has been previously created or creates a new 
Manager.
+     * @param name The name of the Manager to retrieve.
+     * @param factory The Factory to use to create the Manager.
+     * @param data An Object that should be passed to the factory when 
creating the Manager.
+     * @param <M> The Type of the Manager to be created.
+     * @param <T> The type of the Factory data.
+     * @return A Manager with the specified name and type.
+     */
+    public static <M extends AbstractManager, T> M getManager(final String 
name, final ManagerFactory<M, T> factory,
+                                                              final T data) {
+        LOCK.lock();
+        try {
+            @SuppressWarnings("unchecked")
+            M manager = (M) MAP.get(name);
+            if (manager == null) {
+                manager = factory.createManager(name, data);
+                if (manager == null) {
+                    throw new IllegalStateException("ManagerFactory [" + 
factory + "] unable to create manager for ["
+                            + name + "] with data [" + data + "]");
+                }
+                MAP.put(name, manager);
+            }
+            manager.count++;
+            return manager;
+        } finally {
+            LOCK.unlock();
+        }
+    }
+
+    /**
+     * Determines if a Manager with the specified name exists.
+     * @param name The name of the Manager.
+     * @return True if the Manager exists, false otherwise.
+     */
+    public static boolean hasManager(final String name) {
+        LOCK.lock();
+        try {
+            return MAP.containsKey(name);
+        } finally {
+            LOCK.unlock();
+        }
+    }
+
+    /**
+     * May be overridden by Managers to perform processing while the Manager 
is being released and the
+     * lock is held.
+     */
+    protected void releaseSub() {
+    }
+
+    protected int getCount() {
+        return count;
+    }
+
+    /**
+     * Called to signify that this Manager is no longer required by an 
Appender.
+     */
+    public void release() {
+        LOCK.lock();
+        try {
+            --count;
+            if (count <= 0) {
+                MAP.remove(name);
+                LOGGER.debug("Shutting down {} {}", 
this.getClass().getSimpleName(), getName());
+                releaseSub();
+            }
+        } finally {
+            LOCK.unlock();
+        }
+    }
+
+    /**
+     * Returns the name of the Manager.
+     * @return The name of the Manager.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Provide a description of the content format supported by this Manager.  
Default implementation returns an empty
+     * (unspecified) Map.
+     *
+     * @return a Map of key/value pairs describing the Manager-specific 
content format, or an empty Map if no content
+     * format descriptors are specified.
+     */
+    public Map<String, String> getContentFormat() {
+        return new HashMap<>();
+    }
+
+    protected void log(Level level, String message, Throwable throwable) {
+        Message m = LOGGER.getMessageFactory().newMessage("{} {} {}: {}",
+                getClass().getSimpleName(), getName(), message, throwable);
+        LOGGER.log(level, m, throwable);
+    }
+
+    protected void logDebug(String message, Throwable throwable) {
+        log(Level.DEBUG, message, throwable);
+    }
+
+    protected void logError(String message, Throwable throwable) {
+        log(Level.ERROR, message, throwable);
+    }
+
+    protected void logWarn(String message, Throwable throwable) {
+        log(Level.WARN, message, throwable);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3c84ef94/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 608f2e8..ae6a017 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
@@ -24,6 +24,7 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.io.Serializable;
 import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
@@ -44,8 +45,14 @@ public class RollingFileManager extends FileManager {
     private long initialTime;
     private final PatternProcessor patternProcessor;
     private final Semaphore semaphore = new Semaphore(1);
-    private final TriggeringPolicy triggeringPolicy;
-    private final RolloverStrategy rolloverStrategy;
+    private volatile TriggeringPolicy triggeringPolicy;
+    private volatile RolloverStrategy rolloverStrategy;
+
+    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");
 
     protected RollingFileManager(final String fileName, final String pattern, 
final OutputStream os,
             final boolean append, final long size, final long time, final 
TriggeringPolicy triggeringPolicy,
@@ -142,6 +149,17 @@ public class RollingFileManager extends FileManager {
         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
@@ -291,6 +309,24 @@ public class RollingFileManager extends FileManager {
             this.advertiseURI = advertiseURI;
             this.layout = layout;
         }
+
+        public TriggeringPolicy getTriggeringPolicy()
+        {
+            return this.policy;
+        }
+
+        public RolloverStrategy getRolloverStrategy()
+        {
+            return this.strategy;
+        }
+    }
+
+    @Override
+    public void updateData(final Object data)
+    {
+        final FactoryData factoryData = (FactoryData) data;
+        setRolloverStrategy(factoryData.getRolloverStrategy());
+        setTriggeringPolicy(factoryData.getTriggeringPolicy());
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3c84ef94/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java.orig
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java.orig
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java.orig
new file mode 100644
index 0000000..608f2e8
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java.orig
@@ -0,0 +1,343 @@
+/*
+ * 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.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.concurrent.Semaphore;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+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.util.Log4jThread;
+
+/**
+ * The Rolling File Manager.
+ */
+public class RollingFileManager extends FileManager {
+
+    private static RollingFileManagerFactory factory = new 
RollingFileManagerFactory();
+
+    private long size;
+    private long initialTime;
+    private final PatternProcessor patternProcessor;
+    private final Semaphore semaphore = new Semaphore(1);
+    private final TriggeringPolicy triggeringPolicy;
+    private final RolloverStrategy rolloverStrategy;
+
+    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) {
+        super(fileName, os, append, false, advertiseURI, layout, bufferSize, 
writeHeader);
+        this.size = size;
+        this.initialTime = time;
+        this.triggeringPolicy = triggeringPolicy;
+        this.rolloverStrategy = rolloverStrategy;
+        this.patternProcessor = new PatternProcessor(pattern);
+        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
+     * @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) {
+
+        return (RollingFileManager) getManager(fileName, new 
FactoryData(pattern, append,
+            bufferedIO, policy, strategy, advertiseURI, layout, bufferSize), 
factory);
+    }
+
+    @Override
+    protected synchronized void write(final byte[] bytes, final int offset, 
final int length) {
+        size += length;
+        super.write(bytes, offset, length);
+    }
+
+    /**
+     * Returns the current size of the file.
+     * @return The size of the file in bytes.
+     */
+    public long getFileSize() {
+        return size;
+    }
+
+    /**
+     * Returns the time the file was created.
+     * @return The time the file was created.
+     */
+    public long getFileTime() {
+        return initialTime;
+    }
+
+    /**
+     * Determine if a rollover should occur.
+     * @param event The LogEvent.
+     */
+    public synchronized void checkRollover(final LogEvent event) {
+        if (triggeringPolicy.isTriggeringEvent(event)) {
+            rollover();
+        }
+    }
+
+    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  {
+        final OutputStream os = new FileOutputStream(getFileName(), 
isAppend());
+        if (getBufferSize() > 0) { // negative buffer size means no buffering
+            setOutputStream(new BufferedOutputStream(os, getBufferSize()));
+        } else {
+            setOutputStream(os);
+        }
+    }
+
+    /**
+     * Returns the pattern processor.
+     * @return The PatternProcessor.
+     */
+    public PatternProcessor getPatternProcessor() {
+        return patternProcessor;
+    }
+
+    /**
+     * Returns the triggering policy.
+     * @param <T> TriggeringPolicy type
+     * @return The TriggeringPolicy
+     */
+    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;
+        Thread thread = null;
+
+        try {
+            final RolloverDescription descriptor = strategy.rollover(this);
+            if (descriptor != null) {
+                writeFooter();
+                close();
+                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());
+                    thread = new Log4jThread(new 
AsyncAction(descriptor.getAsynchronous(), this));
+                    thread.start();
+                }
+                return true;
+            }
+            return false;
+        } finally {
+            if (thread == null || !thread.isAlive()) {
+                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;
+        }
+
+        /**
+         * Perform 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();
+        }
+    }
+
+    /**
+     * Factory data.
+     */
+    private static class FactoryData {
+        private final String pattern;
+        private final boolean append;
+        private final boolean bufferedIO;
+        private final int bufferSize;
+        private final TriggeringPolicy policy;
+        private final RolloverStrategy strategy;
+        private final String advertiseURI;
+        private final Layout<? extends Serializable> layout;
+
+        /**
+         * Create 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
+         */
+        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) {
+            this.pattern = pattern;
+            this.append = append;
+            this.bufferedIO = bufferedIO;
+            this.bufferSize = bufferSize;
+            this.policy = policy;
+            this.strategy = strategy;
+            this.advertiseURI = advertiseURI;
+            this.layout = layout;
+        }
+    }
+
+    /**
+     * Factory to create a RollingFileManager.
+     */
+    private static class RollingFileManagerFactory implements 
ManagerFactory<RollingFileManager, FactoryData> {
+
+        /**
+         * Create the 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 {
+                file.createNewFile();
+            } catch (final IOException ioe) {
+                LOGGER.error("Unable to create file " + name, ioe);
+                return null;
+            }
+            final long size = data.append ? file.length() : 0;
+
+            OutputStream os;
+            try {
+                os = new FileOutputStream(name, data.append);
+                int bufferSize = data.bufferSize;
+                if (data.bufferedIO) {
+                    os = new BufferedOutputStream(os, bufferSize);
+                } else {
+                    bufferSize = -1; // negative buffer size signals 
bufferedIO was configured false
+                }
+                final long time = file.lastModified(); // LOG4J2-531 create 
file first so time has valid value
+                return new RollingFileManager(name, data.pattern, os, 
data.append, size, time, data.policy,
+                    data.strategy, data.advertiseURI, data.layout, bufferSize, 
writeHeader);
+            } catch (final FileNotFoundException ex) {
+                LOGGER.error("FileManager (" + name + ") " + ex);
+            }
+            return null;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3c84ef94/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java
index dc3261e..b732feb 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java
@@ -247,6 +247,23 @@ public class RollingRandomAccessFileManager extends 
RollingFileManager {
             this.advertiseURI = advertiseURI;
             this.layout = layout;
         }
+
+        public TriggeringPolicy getTriggeringPolicy()
+        {
+            return this.policy;
+        }
+
+        public RolloverStrategy getRolloverStrategy()
+        {
+            return this.strategy;
+        }
     }
 
+    @Override
+    public void updateData(final Object data)
+    {
+        final FactoryData factoryData = (FactoryData) data;
+        setRolloverStrategy(factoryData.getRolloverStrategy());
+        setTriggeringPolicy(factoryData.getTriggeringPolicy());
+    }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3c84ef94/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java.orig
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java.orig
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java.orig
new file mode 100644
index 0000000..dc3261e
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java.orig
@@ -0,0 +1,252 @@
+/*
+ * 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.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.util.NullOutputStream;
+
+/**
+ * Extends RollingFileManager 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 RollingRandomAccessFileManager extends RollingFileManager {
+    /**
+     * The default buffer size.
+     */
+    public static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
+
+    private static final RollingRandomAccessFileManagerFactory FACTORY = new 
RollingRandomAccessFileManagerFactory();
+
+    private final boolean isImmediateFlush;
+    private RandomAccessFile randomAccessFile;
+    private final ByteBuffer buffer;
+    private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
+
+    public RollingRandomAccessFileManager(final RandomAccessFile raf, final 
String fileName, final String pattern,
+            final OutputStream os, final boolean append, final boolean 
immediateFlush, final int bufferSize,
+            final long size, final long time, final TriggeringPolicy policy, 
final RolloverStrategy strategy,
+            final String advertiseURI, final Layout<? extends Serializable> 
layout, final boolean writeHeader) {
+        super(fileName, pattern, os, append, size, time, policy, strategy, 
advertiseURI, layout, bufferSize,
+                writeHeader);
+        this.isImmediateFlush = immediateFlush;
+        this.randomAccessFile = raf;
+        isEndOfBatch.set(Boolean.FALSE);
+        this.buffer = ByteBuffer.allocate(bufferSize);
+        writeHeader();
+    }
+
+    /**
+     * Writes the layout's header to the file if it exists.
+     */
+    private void writeHeader() {
+        if (layout == null) {
+            return;
+        }
+        final byte[] header = layout.getHeader();
+        if (header == null) {
+            return;
+        }
+        try {
+            // write to the file, not to the buffer: the buffer may not be 
empty
+            randomAccessFile.write(header, 0, header.length);
+        } catch (final IOException e) {
+            logError("unable to write header", e);
+        }
+    }
+
+    public static RollingRandomAccessFileManager 
getRollingRandomAccessFileManager(final String fileName,
+            final String filePattern, final boolean isAppend, final boolean 
immediateFlush, final int bufferSize,
+            final TriggeringPolicy policy, final RolloverStrategy strategy, 
final String advertiseURI,
+            final Layout<? extends Serializable> layout) {
+        return (RollingRandomAccessFileManager) getManager(fileName, new 
FactoryData(filePattern, isAppend,
+                immediateFlush, bufferSize, policy, strategy, 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) {
+        super.write(bytes, offset, length); // writes to dummy output stream, 
needed to track file size
+
+        int chunk = 0;
+        do {
+            if (length > buffer.remaining()) {
+                flush();
+            }
+            chunk = Math.min(length, buffer.remaining());
+            buffer.put(bytes, offset, chunk);
+            offset += chunk;
+            length -= chunk;
+        } while (length > 0);
+
+        if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) {
+            flush();
+        }
+    }
+
+    @Override
+    protected void createFileAfterRollover() throws IOException {
+        this.randomAccessFile = new RandomAccessFile(getFileName(), "rw");
+        if (isAppend()) {
+            randomAccessFile.seek(randomAccessFile.length());
+        }
+        writeHeader();
+    }
+
+    @Override
+    public synchronized void flush() {
+        buffer.flip();
+        try {
+            randomAccessFile.write(buffer.array(), 0, buffer.limit());
+        } catch (final IOException ex) {
+            final String msg = "Error writing to RandomAccessFile " + 
getName();
+            throw new AppenderLoggingException(msg, ex);
+        }
+        buffer.clear();
+    }
+
+    @Override
+    public synchronized void close() {
+        flush();
+        try {
+            randomAccessFile.close();
+        } catch (final IOException e) {
+            logError("unable to close RandomAccessFile", e);
+        }
+    }
+
+    /**
+     * Returns the buffer capacity.
+     * 
+     * @return the buffer size
+     */
+    @Override
+    public int getBufferSize() {
+        return buffer.capacity();
+    }
+
+    /**
+     * Factory to create a RollingRandomAccessFileManager.
+     */
+    private static class RollingRandomAccessFileManagerFactory implements
+            ManagerFactory<RollingRandomAccessFileManager, FactoryData> {
+
+        /**
+         * Create the RollingRandomAccessFileManager.
+         *
+         * @param name The name of the entity to manage.
+         * @param data The data required to create the entity.
+         * @return a RollingFileManager.
+         */
+        @Override
+        public RollingRandomAccessFileManager 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 long size = data.append ? file.length() : 0;
+            final long time = file.exists() ? file.lastModified() : 
System.currentTimeMillis();
+
+            final boolean writeHeader = !data.append || !file.exists();
+            RandomAccessFile raf = null;
+            try {
+                raf = new RandomAccessFile(name, "rw");
+                if (data.append) {
+                    final long length = raf.length();
+                    LOGGER.trace("RandomAccessFile {} seek to {}", name, 
length);
+                    raf.seek(length);
+                } else {
+                    LOGGER.trace("RandomAccessFile {} set length to 0", name);
+                    raf.setLength(0);
+                }
+                return new RollingRandomAccessFileManager(raf, name, 
data.pattern, NullOutputStream.NULL_OUTPUT_STREAM,
+                        data.append, data.immediateFlush, data.bufferSize, 
size, time, data.policy, data.strategy,
+                        data.advertiseURI, data.layout, writeHeader);
+            } catch (final IOException ex) {
+                LOGGER.error("Cannot access RandomAccessFile {}) " + ex);
+                if (raf != null) {
+                    try {
+                        raf.close();
+                    } catch (final IOException e) {
+                        LOGGER.error("Cannot close RandomAccessFile {}", name, 
e);
+                    }
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Factory data.
+     */
+    private static class FactoryData {
+        private final String pattern;
+        private final boolean append;
+        private final boolean immediateFlush;
+        private final int bufferSize;
+        private final TriggeringPolicy policy;
+        private final RolloverStrategy strategy;
+        private final String advertiseURI;
+        private final Layout<? extends Serializable> layout;
+
+        /**
+         * Create the data for the factory.
+         *
+         * @param pattern The pattern.
+         * @param append The append flag.
+         * @param immediateFlush
+         * @param bufferSize
+         * @param policy
+         * @param strategy
+         * @param advertiseURI
+         * @param layout
+         */
+        public FactoryData(final String pattern, final boolean append, final 
boolean immediateFlush,
+                final int bufferSize, final TriggeringPolicy policy, final 
RolloverStrategy strategy,
+                final String advertiseURI, final Layout<? extends 
Serializable> layout) {
+            this.pattern = pattern;
+            this.append = append;
+            this.immediateFlush = immediateFlush;
+            this.bufferSize = bufferSize;
+            this.policy = policy;
+            this.strategy = strategy;
+            this.advertiseURI = advertiseURI;
+            this.layout = layout;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3c84ef94/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 1ae3f64..b95c6d8 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -24,6 +24,12 @@
   </properties>
   <body>
     <release version="2.5" date="2015-MM-DD" description="GA Release 2.5">
+      <action issue="LOG4J2-89" dev="rgoers" type="add">
+        Allow rollover to occur at any time. Add CronTriggeringPolicy
+      </action>
+      <action issue="LOG4J2-381" dev="rgoers" type="fix" due-to="Anthony 
Baldocchi">
+        Allow triggering policy and rollover strategy to be modified during 
reconfiguration.
+      </action>
       <action issue="LOG4J2-1136" dev="rgoers" type="add">
         Add support for JSR 223 scripts in filters and the PatternSelector.
       </action>

Reply via email to