Repository: logging-log4j2
Updated Branches:
  refs/heads/master 457f2262e -> 8318a3856


[LOG4J2-1699] Configurable Log File Permissions with
PosixFilePermission. Patch applied with some minor tweaks.

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

Branch: refs/heads/master
Commit: 8318a3856ac6fedb2374fa894b48f5e54ff9d7d9
Parents: 457f226
Author: Pierrick HYMBERT <[email protected]>
Authored: Tue Jun 20 14:39:47 2017 -0700
Committer: Gary Gregory <[email protected]>
Committed: Tue Jun 20 14:39:47 2017 -0700

----------------------------------------------------------------------
 .../log4j/core/appender/FileAppender.java       |  38 ++-
 .../log4j/core/appender/FileManager.java        | 105 ++++++-
 .../core/appender/RollingFileAppender.java      |  40 ++-
 .../RollingRandomAccessFileAppender.java        |  29 +-
 .../appender/rolling/RollingFileManager.java    |  81 ++++--
 .../rolling/RollingRandomAccessFileManager.java |  37 ++-
 .../core/appender/FilePermissionsTest.java      | 133 +++++++++
 .../rolling/CronTriggeringPolicyTest.java       | 278 +++++++++----------
 .../rolling/OnStartupTriggeringPolicyTest.java  |   2 +-
 .../RollingRandomAccessFileManagerTest.java     |  33 ++-
 pom.xml                                         |   2 +-
 src/changes/changes.xml                         |   3 +
 12 files changed, 591 insertions(+), 190 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileAppender.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileAppender.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileAppender.java
index 030126b..8e5736f 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileAppender.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileAppender.java
@@ -70,6 +70,15 @@ public final class FileAppender extends 
AbstractOutputStreamAppender<FileManager
         @PluginBuilderAttribute
         private boolean createOnDemand;
 
+        @PluginBuilderAttribute
+        private String filePermissions;
+
+        @PluginBuilderAttribute
+        private String fileOwner;
+
+        @PluginBuilderAttribute
+        private String fileGroup;
+
         @Override
         public FileAppender build() {
             boolean bufferedIo = isBufferedIo();
@@ -84,7 +93,7 @@ public final class FileAppender extends 
AbstractOutputStreamAppender<FileManager
             final Layout<? extends Serializable> layout = getOrCreateLayout();
 
             final FileManager manager = FileManager.getFileManager(fileName, 
append, locking, bufferedIo, createOnDemand,
-                    advertiseUri, layout, bufferSize, getConfiguration());
+                    advertiseUri, layout, bufferSize, filePermissions, 
fileOwner, fileGroup, getConfiguration());
             if (manager == null) {
                 return null;
             }
@@ -117,6 +126,18 @@ public final class FileAppender extends 
AbstractOutputStreamAppender<FileManager
             return locking;
         }
 
+        public String getFilePermissions() {
+            return filePermissions;
+        }
+
+        public String getFileOwner() {
+            return fileOwner;
+        }
+
+        public String getFileGroup() {
+            return fileGroup;
+        }
+
         public B withAdvertise(final boolean advertise) {
             this.advertise = advertise;
             return asBuilder();
@@ -147,6 +168,21 @@ public final class FileAppender extends 
AbstractOutputStreamAppender<FileManager
             return asBuilder();
         }
 
+        public B withFilePermissions(final String filePermissions) {
+            this.filePermissions = filePermissions;
+            return asBuilder();
+        }
+
+        public B withFileOwner(final String fileOwner) {
+            this.fileOwner = fileOwner;
+            return asBuilder();
+        }
+
+        public B withFileGroup(final String fileGroup) {
+            this.fileGroup = fileGroup;
+            return asBuilder();
+        }
+
     }
     
     private static final int DEFAULT_BUFFER_SIZE = 8192;

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileManager.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileManager.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileManager.java
index c0b8adb..c07afc2 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileManager.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileManager.java
@@ -17,7 +17,6 @@
 package org.apache.logging.log4j.core.appender;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -25,9 +24,20 @@ import java.io.Serializable;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.GroupPrincipal;
+import java.nio.file.attribute.PosixFileAttributeView;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.nio.file.attribute.UserPrincipal;
+import java.nio.file.attribute.UserPrincipalLookupService;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LoggerContext;
@@ -48,6 +58,9 @@ public class FileManager extends OutputStreamManager {
     private final boolean isLocking;
     private final String advertiseURI;
     private final int bufferSize;
+    private final Set<PosixFilePermission> filePermissions;
+    private final String fileOwner;
+    private final String fileGroup;
 
     /**
      * @deprecated
@@ -73,11 +86,16 @@ public class FileManager extends OutputStreamManager {
         this.isLocking = locking;
         this.advertiseURI = advertiseURI;
         this.bufferSize = buffer.capacity();
+        this.filePermissions = null;
+        this.fileOwner = null;
+        this.fileGroup = null;
     }
 
     /**
+     * @deprecated
      * @since 2.7
      */
+    @Deprecated
     protected FileManager(final LoggerContext loggerContext, final String 
fileName, final OutputStream os, final boolean append, final boolean locking,
             final boolean createOnDemand, final String advertiseURI, final 
Layout<? extends Serializable> layout,
             final boolean writeHeader, final ByteBuffer buffer) {
@@ -87,6 +105,30 @@ public class FileManager extends OutputStreamManager {
         this.isLocking = locking;
         this.advertiseURI = advertiseURI;
         this.bufferSize = buffer.capacity();
+        this.filePermissions = null;
+        this.fileOwner = null;
+        this.fileGroup = null;
+    }
+
+    /**
+     * @since 2.8.3
+     */
+    protected FileManager(final LoggerContext loggerContext, final String 
fileName, final OutputStream os, final boolean append, final boolean locking,
+            final boolean createOnDemand, final String advertiseURI, final 
Layout<? extends Serializable> layout,
+            final String filePermissions, final String fileOwner, final String 
fileGroup, final boolean writeHeader,
+            final ByteBuffer buffer) {
+        super(loggerContext, os, fileName, createOnDemand, layout, 
writeHeader, buffer);
+        this.isAppend = append;
+        this.createOnDemand = createOnDemand;
+        this.isLocking = locking;
+        this.advertiseURI = advertiseURI;
+        this.bufferSize = buffer.capacity();
+
+        final Set<String> views = 
FileSystems.getDefault().supportedFileAttributeViews();
+        this.filePermissions = filePermissions != null && 
views.contains("posix")
+                                ? 
PosixFilePermissions.fromString(filePermissions) : null;
+        this.fileOwner = views.contains("owner") ? fileOwner : null;
+        this.fileGroup = views.contains("group") ? fileGroup : null;
     }
 
     /**
@@ -99,25 +141,62 @@ public class FileManager extends OutputStreamManager {
      * @param advertiseUri the URI to use when advertising the file
      * @param layout The layout
      * @param bufferSize buffer size for buffered IO
+     * @param filePermissions File permissions
+     * @param fileOwner File owner
+     * @param fileOwner File group
      * @param configuration The configuration.
      * @return A FileManager for the File.
      */
     public static FileManager getFileManager(final String fileName, final 
boolean append, boolean locking,
             final boolean bufferedIo, final boolean createOnDemand, final 
String advertiseUri,
-            final Layout<? extends Serializable> layout, final int bufferSize, 
final Configuration configuration) {
+            final Layout<? extends Serializable> layout,
+            final int bufferSize, final String filePermissions, final String 
fileOwner, final String fileGroup,
+            final Configuration configuration) {
 
         if (locking && bufferedIo) {
             locking = false;
         }
         return (FileManager) getManager(fileName, new FactoryData(append, 
locking, bufferedIo, bufferSize,
-                createOnDemand, advertiseUri, layout, configuration), FACTORY);
+                createOnDemand, advertiseUri, layout, filePermissions, 
fileOwner, fileGroup, configuration), FACTORY);
     }
 
     @Override
-    protected OutputStream createOutputStream() throws FileNotFoundException {
-        String filename = getFileName();
+    protected OutputStream createOutputStream() throws IOException {
+        final String filename = getFileName();
         LOGGER.debug("Now writing to {} at {}", filename, new Date());
-        return new FileOutputStream(filename, isAppend);
+        final FileOutputStream fos = new FileOutputStream(filename, isAppend);
+        definePathAttributeView(Paths.get(filename));
+        return fos;
+    }
+
+    protected void definePathAttributeView(final Path path) throws IOException 
{
+      if (filePermissions != null || fileOwner != null || fileGroup != null) {
+        // FileOutputStream may not create new file on all jvm
+        path.toFile().createNewFile();
+
+        final PosixFileAttributeView view = Files.getFileAttributeView(path,
+            PosixFileAttributeView.class);
+        if (view != null) {
+          final UserPrincipalLookupService lookupService = 
FileSystems.getDefault()
+              .getUserPrincipalLookupService();
+          if (fileOwner != null) {
+            final UserPrincipal userPrincipal = 
lookupService.lookupPrincipalByName(fileOwner);
+            if (userPrincipal != null) {
+              view.setOwner(userPrincipal);
+            }
+          }
+          if (fileGroup != null) {
+            final GroupPrincipal groupPrincipal = lookupService
+                .lookupPrincipalByGroupName(fileGroup);
+            if (groupPrincipal != null) {
+              view.setGroup(groupPrincipal);
+            }
+          }
+          if (filePermissions != null) {
+            view.setPermissions(filePermissions);
+          }
+        }
+      }
     }
 
     @Override
@@ -154,6 +233,7 @@ public class FileManager extends OutputStreamManager {
      * @param length how many bytes to write
      * @since 2.8
      */
+    @Override
     protected synchronized void writeToDestination(final byte[] bytes, final 
int offset, final int length) {
         if (isLocking) {
             try {
@@ -242,6 +322,9 @@ public class FileManager extends OutputStreamManager {
         private final boolean createOnDemand;
         private final String advertiseURI;
         private final Layout<? extends Serializable> layout;
+        private final String filePermissions;
+        private final String fileOwner;
+        private final String fileGroup;
 
         /**
          * Constructor.
@@ -252,10 +335,14 @@ public class FileManager extends OutputStreamManager {
          * @param createOnDemand if you want to lazy-create the file (a.k.a. 
on-demand.)
          * @param advertiseURI the URI to use when advertising the file
          * @param layout The layout
+         * @param filePermissions File permissions
+         * @param fileOwner File owner
+         * @param fileGroup File group
          * @param configuration the configuration
          */
         public FactoryData(final boolean append, final boolean locking, final 
boolean bufferedIo, final int bufferSize,
                 final boolean createOnDemand, final String advertiseURI, final 
Layout<? extends Serializable> layout,
+                final String filePermissions, final String fileOwner, final 
String fileGroup,
                 final Configuration configuration) {
             super(configuration);
             this.append = append;
@@ -265,6 +352,9 @@ public class FileManager extends OutputStreamManager {
             this.createOnDemand = createOnDemand;
             this.advertiseURI = advertiseURI;
             this.layout = layout;
+            this.filePermissions = filePermissions;
+            this.fileOwner = fileOwner;
+            this.fileGroup = fileGroup;
         }
     }
 
@@ -289,7 +379,8 @@ public class FileManager extends OutputStreamManager {
                 final ByteBuffer byteBuffer = ByteBuffer.wrap(new 
byte[actualSize]);
                 final FileOutputStream fos = data.createOnDemand ? null : new 
FileOutputStream(file, data.append);
                 return new FileManager(data.getLoggerContext(), name, fos, 
data.append, data.locking,
-                        data.createOnDemand, data.advertiseURI, data.layout, 
writeHeader, byteBuffer);
+                        data.createOnDemand, data.advertiseURI, data.layout,
+                        data.filePermissions, data.fileOwner, data.fileGroup, 
writeHeader, byteBuffer);
             } catch (final IOException ex) {
                 LOGGER.error("FileManager (" + name + ") " + ex, ex);
             }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java
index c3e092a..1072259 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java
@@ -28,11 +28,11 @@ import org.apache.logging.log4j.core.Filter;
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import 
org.apache.logging.log4j.core.appender.rolling.DirectFileRolloverStrategy;
 import 
org.apache.logging.log4j.core.appender.rolling.DirectWriteRolloverStrategy;
 import org.apache.logging.log4j.core.appender.rolling.RollingFileManager;
 import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
 import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
-import 
org.apache.logging.log4j.core.appender.rolling.DirectFileRolloverStrategy;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
@@ -89,6 +89,15 @@ public final class RollingFileAppender extends 
AbstractOutputStreamAppender<Roll
         @PluginBuilderAttribute
         private boolean createOnDemand;
 
+        @PluginBuilderAttribute
+        private String filePermissions;
+
+        @PluginBuilderAttribute
+        private String fileOwner;
+
+        @PluginBuilderAttribute
+        private String fileGroup;
+
         @Override
         public RollingFileAppender build() {
             // Even though some variables may be annotated with @Required, we 
must still perform validation here for
@@ -130,7 +139,7 @@ public final class RollingFileAppender extends 
AbstractOutputStreamAppender<Roll
             final Layout<? extends Serializable> layout = getOrCreateLayout();
             final RollingFileManager manager = 
RollingFileManager.getFileManager(fileName, filePattern, append,
                     isBufferedIo, policy, strategy, advertiseUri, layout, 
bufferSize, isImmediateFlush(),
-                    createOnDemand, getConfiguration());
+                    createOnDemand, filePermissions, fileOwner, fileGroup, 
getConfiguration());
             if (manager == null) {
                 return null;
             }
@@ -165,6 +174,18 @@ public final class RollingFileAppender extends 
AbstractOutputStreamAppender<Roll
             return locking;
         }
 
+        public String getFilePermissions() {
+            return filePermissions;
+        }
+
+        public String getFileOwner() {
+            return fileOwner;
+        }
+
+        public String getFileGroup() {
+            return fileGroup;
+        }
+
         public B withAdvertise(final boolean advertise) {
             this.advertise = advertise;
             return asBuilder();
@@ -222,6 +243,21 @@ public final class RollingFileAppender extends 
AbstractOutputStreamAppender<Roll
             return asBuilder();
         }
 
+        public B withFilePermissions(final String filePermissions) {
+            this.filePermissions = filePermissions;
+            return asBuilder();
+        }
+
+        public B withFileOwner(final String fileOwner) {
+            this.fileOwner = fileOwner;
+            return asBuilder();
+        }
+
+        public B withFileGroup(final String fileGroup) {
+            this.fileGroup = fileGroup;
+            return asBuilder();
+        }
+
     }
     
     private static final int DEFAULT_BUFFER_SIZE = 8192;

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java
index f0416ce..89c2008 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java
@@ -33,11 +33,9 @@ import 
org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
 import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
 import org.apache.logging.log4j.core.config.plugins.PluginElement;
-import org.apache.logging.log4j.core.layout.PatternLayout;
 import org.apache.logging.log4j.core.net.Advertiser;
 import org.apache.logging.log4j.core.util.Booleans;
 import org.apache.logging.log4j.core.util.Integers;
@@ -80,6 +78,15 @@ public final class RollingRandomAccessFileAppender extends 
AbstractOutputStreamA
         @PluginBuilderAttribute("advertiseURI")
         private String advertiseURI;
 
+        @PluginBuilderAttribute
+        private String filePermissions;
+
+        @PluginBuilderAttribute
+        private String fileOwner;
+
+        @PluginBuilderAttribute
+        private String fileGroup;
+
         @Override
         public RollingRandomAccessFileAppender build() {
             final String name = getName();
@@ -114,7 +121,8 @@ public final class RollingRandomAccessFileAppender extends 
AbstractOutputStreamA
             final int bufferSize = getBufferSize();
             final RollingRandomAccessFileManager manager = 
RollingRandomAccessFileManager
                     .getRollingRandomAccessFileManager(fileName, filePattern, 
append, immediateFlush, bufferSize, policy,
-                            strategy, advertiseURI, layout, 
getConfiguration());
+                            strategy, advertiseURI, layout,
+                            filePermissions, fileOwner, fileGroup, 
getConfiguration());
             if (manager == null) {
                 return null;
             }
@@ -160,6 +168,21 @@ public final class RollingRandomAccessFileAppender extends 
AbstractOutputStreamA
             return asBuilder();
         }
 
+        public B withFilePermissions(final String filePermissions) {
+            this.filePermissions = filePermissions;
+            return asBuilder();
+        }
+
+        public B withFileOwner(final String fileOwner) {
+            this.fileOwner = fileOwner;
+            return asBuilder();
+        }
+
+        public B withFileGroup(final String fileGroup) {
+            this.fileGroup = fileGroup;
+            return asBuilder();
+        }
+
     }
     
     private final String fileName;

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/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 6e83abb..e0d0360 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
@@ -64,10 +64,11 @@ public class RollingFileManager extends FileManager {
     private volatile boolean renameEmptyFiles = false;
     private volatile boolean initialized = false;
     private volatile String fileName;
-    private FileExtension fileExtension;
+    private final FileExtension fileExtension;
+
     /* This executor pool will create a new Thread for every work async action 
to be performed. Using it allows
        us to make sure all the Threads are completed when the Manager is 
stopped. */
-    private ExecutorService asyncExecutor = new ThreadPoolExecutor(0, 
Integer.MAX_VALUE, 0, TimeUnit.MILLISECONDS,
+    private final ExecutorService asyncExecutor = new ThreadPoolExecutor(0, 
Integer.MAX_VALUE, 0, TimeUnit.MILLISECONDS,
             new EmptyQueue(), threadFactory);
 
     private static final AtomicReferenceFieldUpdater<RollingFileManager, 
TriggeringPolicy> triggeringPolicyUpdater =
@@ -101,9 +102,7 @@ public class RollingFileManager extends FileManager {
         this.fileExtension = FileExtension.lookupForFile(pattern);
     }
 
-    /**
-     * @since 2.7
-     */
+    @Deprecated
     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,
@@ -119,6 +118,27 @@ public class RollingFileManager extends FileManager {
         this.fileExtension = FileExtension.lookupForFile(pattern);
     }
 
+    /**
+     * @since 2.8.3
+     */
+    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 String filePermissions, final String fileOwner, final String 
fileGroup,
+            final boolean writeHeader, final ByteBuffer buffer) {
+        super(loggerContext, fileName, os, append, false, createOnDemand, 
advertiseURI, layout,
+              filePermissions, fileOwner, fileGroup, writeHeader, buffer);
+        this.size = size;
+        this.initialTime = time;
+        this.triggeringPolicy = triggeringPolicy;
+        this.rolloverStrategy = rolloverStrategy;
+        this.patternProcessor = new PatternProcessor(pattern);
+        this.patternProcessor.setPrevFileTime(time);
+        this.fileName = fileName;
+        this.fileExtension = FileExtension.lookupForFile(pattern);
+    }
+
     public void initialize() {
 
         if (!initialized) {
@@ -144,16 +164,22 @@ public class RollingFileManager extends FileManager {
      * @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 filePermissions File permissions
+     * @param fileOwner File owner
+     * @param fileGroup File group
      * @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) {
-        String name = fileName == null ? pattern : fileName;
+            final boolean immediateFlush, final boolean createOnDemand,
+            final String filePermissions, final String fileOwner, final String 
fileGroup,
+            final Configuration configuration) {
+        final String name = fileName == null ? pattern : fileName;
         return (RollingFileManager) getManager(name, new FactoryData(fileName, 
pattern, append,
-            bufferedIO, policy, strategy, advertiseURI, layout, bufferSize, 
immediateFlush, createOnDemand, configuration), factory);
+            bufferedIO, policy, strategy, advertiseURI, layout, bufferSize, 
immediateFlush, createOnDemand,
+            filePermissions, fileOwner, fileGroup, configuration), factory);
     }
 
     /**
@@ -229,12 +255,12 @@ public class RollingFileManager extends FileManager {
             ((LifeCycle) triggeringPolicy).stop();
             stopped &= true;
         }
-        boolean status = super.releaseSub(timeout, timeUnit) && stopped;
+        final boolean status = super.releaseSub(timeout, timeUnit) && stopped;
         asyncExecutor.shutdown();
         try {
             // Allow at least the minimum interval to pass so async actions 
can complete.
-            long millis = timeUnit.toMillis(timeout);
-            long waitInterval = MIN_DURATION < millis ? millis : MIN_DURATION;
+            final long millis = timeUnit.toMillis(timeout);
+            final long waitInterval = MIN_DURATION < millis ? millis : 
MIN_DURATION;
 
             for (int count = 1; count <= MAX_TRIES && 
!asyncExecutor.isTerminated(); ++count) {
                 asyncExecutor.awaitTermination(waitInterval * count, 
TimeUnit.MILLISECONDS);
@@ -300,7 +326,7 @@ public class RollingFileManager extends FileManager {
 
     public void setTriggeringPolicy(final TriggeringPolicy triggeringPolicy) {
         triggeringPolicy.initialize(this);
-        TriggeringPolicy policy = this.triggeringPolicy;
+        final TriggeringPolicy policy = this.triggeringPolicy;
         int count = 0;
         boolean policyUpdated = false;
         do {
@@ -474,6 +500,9 @@ public class RollingFileManager extends FileManager {
         private final RolloverStrategy strategy;
         private final String advertiseURI;
         private final Layout<? extends Serializable> layout;
+        private final String filePermissions;
+        private final String fileOwner;
+        private final String fileGroup;
 
         /**
          * Creates the data for the factory.
@@ -485,12 +514,16 @@ public class RollingFileManager extends FileManager {
          * @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 filePermissions File permissions
+         * @param fileOwner File owner
+         * @param fileGroup File group
          * @param configuration The configuration
          */
         public FactoryData(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) {
+                final boolean createOnDemand, final String filePermissions, 
final String fileOwner, final String fileGroup,
+                final Configuration configuration) {
             super(configuration);
             this.fileName = fileName;
             this.pattern = pattern;
@@ -503,6 +536,9 @@ public class RollingFileManager extends FileManager {
             this.layout = layout;
             this.immediateFlush = immediateFlush;
             this.createOnDemand = createOnDemand;
+            this.filePermissions = filePermissions;
+            this.fileOwner = fileOwner;
+            this.fileGroup = fileGroup;
         }
 
         public TriggeringPolicy getTriggeringPolicy()
@@ -535,6 +571,10 @@ public class RollingFileManager extends FileManager {
             builder.append(advertiseURI);
             builder.append(", layout=");
             builder.append(layout);
+            builder.append(", filePermissions=");
+            builder.append(filePermissions);
+            builder.append(", fileOwner=");
+            builder.append(fileOwner);
             builder.append("]");
             return builder.toString();
         }
@@ -590,7 +630,7 @@ public class RollingFileManager extends FileManager {
 
                 return new RollingFileManager(data.getLoggerContext(), 
data.fileName, data.pattern, os,
                         data.append, data.createOnDemand, size, time, 
data.policy, data.strategy, data.advertiseURI,
-                        data.layout, writeHeader, buffer);
+                        data.layout, data.filePermissions, data.fileOwner, 
data.fileGroup, writeHeader, buffer);
             } catch (final IOException ex) {
                 LOGGER.error("RollingFileManager (" + name + ") " + ex, ex);
             }
@@ -600,6 +640,11 @@ public class RollingFileManager extends FileManager {
 
     private static class EmptyQueue extends ArrayBlockingQueue<Runnable> {
 
+        /**
+         * 
+         */
+        private static final long serialVersionUID = 1L;
+
         EmptyQueue() {
             super(1);
         }
@@ -610,24 +655,24 @@ public class RollingFileManager extends FileManager {
         }
 
         @Override
-        public boolean add(Runnable runnable) {
+        public boolean add(final Runnable runnable) {
             throw new IllegalStateException("Queue is full");
         }
 
         @Override
-        public void put(Runnable runnable) throws InterruptedException {
+        public void put(final Runnable runnable) throws InterruptedException {
             /* No point in going into a permanent wait */
             throw new InterruptedException("Unable to insert into queue");
         }
 
         @Override
-        public boolean offer(Runnable runnable, long timeout, TimeUnit 
timeUnit) throws InterruptedException {
+        public boolean offer(final Runnable runnable, final long timeout, 
final TimeUnit timeUnit) throws InterruptedException {
             Thread.sleep(timeUnit.toMillis(timeout));
             return false;
         }
 
         @Override
-        public boolean addAll(Collection<? extends Runnable> collection) {
+        public boolean addAll(final Collection<? extends Runnable> collection) 
{
             if (collection.size() > 0) {
                 throw new IllegalArgumentException("Too many items in 
collection");
             }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/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 21c8918..f210bf5 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
@@ -47,12 +47,28 @@ public class RollingRandomAccessFileManager extends 
RollingFileManager {
     private RandomAccessFile randomAccessFile;
     private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
 
+    @Deprecated
     public RollingRandomAccessFileManager(final LoggerContext loggerContext, 
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) {
+        this(loggerContext, raf, fileName, pattern, os, append, 
immediateFlush, bufferSize, size, time, policy, strategy, advertiseURI,
+               layout, null, null, null, writeHeader);
+    }
+
+    /**
+     * @since 2.8.3
+     */
+    public RollingRandomAccessFileManager(final LoggerContext loggerContext, 
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 String filePermissions, final String fileOwner, final String 
fileGroup,
+            final boolean writeHeader) {
         super(loggerContext, fileName, pattern, os, append, false, size, time, 
policy, strategy, advertiseURI, layout,
+                filePermissions, fileOwner, fileGroup,
                 writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
         this.randomAccessFile = raf;
         isEndOfBatch.set(Boolean.FALSE);
@@ -83,9 +99,11 @@ public class RollingRandomAccessFileManager extends 
RollingFileManager {
     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, final Configuration 
configuration) {
+            final Layout<? extends Serializable> layout, final String 
filePermissions, final String fileOwner, final String fileGroup,
+            final Configuration configuration) {
         return (RollingRandomAccessFileManager) getManager(fileName, new 
FactoryData(filePattern, isAppend,
-                immediateFlush, bufferSize, policy, strategy, advertiseURI, 
layout, configuration), FACTORY);
+                immediateFlush, bufferSize, policy, strategy, advertiseURI, 
layout,
+                filePermissions, fileOwner, fileGroup, configuration), 
FACTORY);
     }
 
     public Boolean isEndOfBatch() {
@@ -188,7 +206,7 @@ public class RollingRandomAccessFileManager extends 
RollingFileManager {
                 }
                 return new 
RollingRandomAccessFileManager(data.getLoggerContext(), raf, name, data.pattern,
                         NullOutputStream.getInstance(), data.append, 
data.immediateFlush, data.bufferSize, size, time, data.policy,
-                        data.strategy, data.advertiseURI, data.layout, 
writeHeader);
+                        data.strategy, data.advertiseURI, data.layout, 
data.filePermissions, data.fileOwner, data.fileGroup, writeHeader);
             } catch (final IOException ex) {
                 LOGGER.error("Cannot access RandomAccessFile " + ex, ex);
                 if (raf != null) {
@@ -215,6 +233,9 @@ public class RollingRandomAccessFileManager extends 
RollingFileManager {
         private final RolloverStrategy strategy;
         private final String advertiseURI;
         private final Layout<? extends Serializable> layout;
+        private final String filePermissions;
+        private final String fileOwner;
+        private final String fileGroup;
 
         /**
          * Create the data for the factory.
@@ -227,11 +248,16 @@ public class RollingRandomAccessFileManager extends 
RollingFileManager {
          * @param strategy
          * @param advertiseURI
          * @param layout
+         * @param filePermissions File permissions
+         * @param fileOwner File owner
+         * @param fileGroup File group
          * @param configuration
          */
         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, final Configuration configuration) {
+                final String advertiseURI, final Layout<? extends 
Serializable> layout,
+                final String filePermissions, final String fileOwner, final 
String fileGroup,
+                final Configuration configuration) {
             super(configuration);
             this.pattern = pattern;
             this.append = append;
@@ -241,6 +267,9 @@ public class RollingRandomAccessFileManager extends 
RollingFileManager {
             this.strategy = strategy;
             this.advertiseURI = advertiseURI;
             this.layout = layout;
+            this.filePermissions = filePermissions;
+            this.fileOwner = fileOwner;
+            this.fileGroup = fileGroup;
         }
 
         public TriggeringPolicy getTriggeringPolicy()

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/FilePermissionsTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/FilePermissionsTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/FilePermissionsTest.java
new file mode 100644
index 0000000..df35c3b
--- /dev/null
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/FilePermissionsTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.junit.CleanFiles;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Tests {@link FileAppender}.
+ */
+@RunWith(Parameterized.class)
+public class FilePermissionsTest {
+
+  @Parameterized.Parameters(name = "{0} {1}")
+  public static Collection<Object[]> data() {
+      return Arrays.asList(new Object[][] { //
+              // @formatter:off
+             {"rwxrwxrwx", true},
+             {"rw-rw-r--", false},
+             {"rw-------", true},
+              });
+              // @formatter:on
+  }
+
+  private final boolean createOnDemand;
+  private final String filePermissions;
+
+    public FilePermissionsTest(final String filePermissions,  final boolean 
createOnDemand) {
+      this.filePermissions = filePermissions;
+      this.createOnDemand = createOnDemand;
+    }
+
+    private static final String FILE_NAME = "target/fileAppenderTest.log";
+    private static final Path PATH = Paths.get(FILE_NAME);
+
+    @Rule
+    public CleanFiles files = new CleanFiles(PATH);
+
+    @AfterClass
+    public static void cleanupClass() {
+        assertTrue("Manager for " + FILE_NAME + " not removed", 
!AbstractManager.hasManager(FILE_NAME));
+    }
+
+    @Test
+    public void testFilePermissions() throws Exception {
+        if 
(!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
+          return;
+        }
+        final Layout<String> layout = 
PatternLayout.newBuilder().withPattern(PatternLayout.SIMPLE_CONVERSION_PATTERN)
+            .build();
+        // @formatter:off
+        final FileAppender appender = FileAppender.newBuilder()
+            .withFileName(FILE_NAME)
+            .withName("test")
+            .withImmediateFlush(false)
+            .withIgnoreExceptions(false)
+            .withBufferedIo(false)
+            .withBufferSize(1)
+            .withLayout(layout)
+            .withCreateOnDemand(createOnDemand)
+            .withFilePermissions(filePermissions)
+            .build();
+        // @formatter:on
+        try {
+            appender.start();
+            final File file = new File(FILE_NAME);
+            assertTrue("Appender did not start", appender.isStarted());
+            Assert.assertNotEquals(createOnDemand, Files.exists(PATH));
+            long curLen = file.length();
+            long prevLen = curLen;
+            assertTrue("File length: " + curLen, curLen == 0);
+            for (int i = 0; i < 100; ++i) {
+                final LogEvent event = 
Log4jLogEvent.newBuilder().setLoggerName("TestLogger") //
+                        
.setLoggerFqcn(FilePermissionsTest.class.getName()).setLevel(Level.INFO) //
+                        .setMessage(new 
SimpleMessage("Test")).setThreadName(this.getClass().getSimpleName()) //
+                        .setTimeMillis(System.currentTimeMillis()).build();
+                try {
+                    appender.append(event);
+                    curLen = file.length();
+                    assertTrue("File length: " + curLen, curLen > prevLen);
+                    // Give up control long enough for another thread/process 
to occasionally do something.
+                    Thread.sleep(25);
+                } catch (final Exception ex) {
+                    throw ex;
+                }
+                prevLen = curLen;
+            }
+            assertEquals(filePermissions,
+                
PosixFilePermissions.toString(Files.getPosixFilePermissions(PATH)));
+        } finally {
+            appender.stop();
+        }
+        assertFalse("Appender did not stop", appender.isStarted());
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicyTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicyTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicyTest.java
index 828f78b..f6bf296 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicyTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicyTest.java
@@ -1,139 +1,139 @@
-/*
- * 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 org.apache.logging.log4j.core.appender.RollingFileAppender;
-import org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender;
-import org.apache.logging.log4j.core.config.Configurator;
-import org.apache.logging.log4j.core.config.NullConfiguration;
-import org.apache.logging.log4j.core.layout.PatternLayout;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-public class CronTriggeringPolicyTest {
-
-    private static final String CRON_EXPRESSION = "0 0 0 * * ?";
-    
-    private NullConfiguration configuration;
-
-     // TODO Need a CleanRegexFiles("testcmd.\\.log\\..*");
-     //@Rule
-     //public CleanFiles cleanFiles = new CleanFiles("testcmd1.log", 
"testcmd2.log", "testcmd3.log");
-
-    @Before
-    public void before() {
-        configuration = new NullConfiguration();
-    }
-
-    private CronTriggeringPolicy createPolicy() {
-        return CronTriggeringPolicy.createPolicy(configuration, 
Boolean.TRUE.toString(), CRON_EXPRESSION);
-    }
-
-    private DefaultRolloverStrategy createStrategy() {
-        return DefaultRolloverStrategy.createStrategy("7", "1", "max", null, 
null, false, configuration);
-    }
-
-    private void testBuilder() {
-        // @formatter:off
-        final RollingFileAppender raf = RollingFileAppender.newBuilder()
-            .withName("test1")
-            .withFileName("target/testcmd1.log")
-            .withFilePattern("target/testcmd1.log.%d{yyyy-MM-dd}")
-            .withPolicy(createPolicy())
-            .withStrategy(createStrategy())
-            .setConfiguration(configuration)
-            .build();
-        // @formatter:on
-        Assert.assertNotNull(raf);
-    }
-
-    /**
-     * Tests LOG4J2-1474 CronTriggeringPolicy raise exception and fail to 
rollover log file when evaluateOnStartup is
-     * true.
-     */
-    @Test
-    public void testBuilderOnce() {
-        testBuilder();
-    }
-
-    /**
-     * Tests LOG4J2-1740 Add CronTriggeringPolicy programmatically leads to NPE
-     */
-    @Test
-    public void testLoggerContextAndBuilder() {
-        Configurator.initialize(configuration);
-        testBuilder();
-    }
-
-    /**
-     * Tests LOG4J2-1740 Add CronTriggeringPolicy programmatically leads to NPE
-     */
-    @Test
-    public void testRollingRandomAccessFileAppender() {
-        // @formatter:off
-        RollingRandomAccessFileAppender.newBuilder()
-            .withName("test2")
-            .withFileName("target/testcmd2.log")
-            .withFilePattern("target/testcmd2.log.%d{yyyy-MM-dd}")
-            .withPolicy(createPolicy())
-            .withStrategy(createStrategy())
-            .setConfiguration(configuration)
-            .build();
-        // @formatter:on
-    }
-
-    
-    /**
-     * Tests LOG4J2-1474 CronTriggeringPolicy raise exception and fail to 
rollover log file when evaluateOnStartup is
-     * true.
-     */
-    @Test
-    public void testBuilderSequence() {
-        testBuilder();
-        testBuilder();
-    }
-
-    private void testFactoryMethod() {
-        final CronTriggeringPolicy triggerPolicy = createPolicy();
-        final DefaultRolloverStrategy rolloverStrategy = createStrategy();
-
-        try (RollingFileManager fileManager = 
RollingFileManager.getFileManager("target/testcmd3.log",
-                "target/testcmd3.log.%d{yyyy-MM-dd}", true, true, 
triggerPolicy, rolloverStrategy, null,
-                PatternLayout.createDefaultLayout(), 0, true, false, 
configuration)) {
-            // trigger rollover
-            fileManager.initialize();
-            fileManager.rollover();
-        }
-    }
-
-    /**
-     * Tests LOG4J2-1474 CronTriggeringPolicy raise exception and fail to 
rollover log file when evaluateOnStartup is
-     * true.
-     */
-    @Test
-    public void testFactoryMethodOnce() {
-        testFactoryMethod();
-    }
-
-    @Test
-    public void testFactoryMethodSequence() {
-        testFactoryMethod();
-        testFactoryMethod();
-    }
-}
+/*
+ * 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 org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.config.NullConfiguration;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CronTriggeringPolicyTest {
+
+    private static final String CRON_EXPRESSION = "0 0 0 * * ?";
+    
+    private NullConfiguration configuration;
+
+     // TODO Need a CleanRegexFiles("testcmd.\\.log\\..*");
+     //@Rule
+     //public CleanFiles cleanFiles = new CleanFiles("testcmd1.log", 
"testcmd2.log", "testcmd3.log");
+
+    @Before
+    public void before() {
+        configuration = new NullConfiguration();
+    }
+
+    private CronTriggeringPolicy createPolicy() {
+        return CronTriggeringPolicy.createPolicy(configuration, 
Boolean.TRUE.toString(), CRON_EXPRESSION);
+    }
+
+    private DefaultRolloverStrategy createStrategy() {
+        return DefaultRolloverStrategy.createStrategy("7", "1", "max", null, 
null, false, configuration);
+    }
+
+    private void testBuilder() {
+        // @formatter:off
+        final RollingFileAppender raf = RollingFileAppender.newBuilder()
+            .withName("test1")
+            .withFileName("target/testcmd1.log")
+            .withFilePattern("target/testcmd1.log.%d{yyyy-MM-dd}")
+            .withPolicy(createPolicy())
+            .withStrategy(createStrategy())
+            .setConfiguration(configuration)
+            .build();
+        // @formatter:on
+        Assert.assertNotNull(raf);
+    }
+
+    /**
+     * Tests LOG4J2-1474 CronTriggeringPolicy raise exception and fail to 
rollover log file when evaluateOnStartup is
+     * true.
+     */
+    @Test
+    public void testBuilderOnce() {
+        testBuilder();
+    }
+
+    /**
+     * Tests LOG4J2-1740 Add CronTriggeringPolicy programmatically leads to NPE
+     */
+    @Test
+    public void testLoggerContextAndBuilder() {
+        Configurator.initialize(configuration);
+        testBuilder();
+    }
+
+    /**
+     * Tests LOG4J2-1740 Add CronTriggeringPolicy programmatically leads to NPE
+     */
+    @Test
+    public void testRollingRandomAccessFileAppender() {
+        // @formatter:off
+        RollingRandomAccessFileAppender.newBuilder()
+            .withName("test2")
+            .withFileName("target/testcmd2.log")
+            .withFilePattern("target/testcmd2.log.%d{yyyy-MM-dd}")
+            .withPolicy(createPolicy())
+            .withStrategy(createStrategy())
+            .setConfiguration(configuration)
+            .build();
+        // @formatter:on
+    }
+
+    
+    /**
+     * Tests LOG4J2-1474 CronTriggeringPolicy raise exception and fail to 
rollover log file when evaluateOnStartup is
+     * true.
+     */
+    @Test
+    public void testBuilderSequence() {
+        testBuilder();
+        testBuilder();
+    }
+
+    private void testFactoryMethod() {
+        final CronTriggeringPolicy triggerPolicy = createPolicy();
+        final DefaultRolloverStrategy rolloverStrategy = createStrategy();
+
+        try (RollingFileManager fileManager = 
RollingFileManager.getFileManager("target/testcmd3.log",
+                "target/testcmd3.log.%d{yyyy-MM-dd}", true, true, 
triggerPolicy, rolloverStrategy, null,
+                PatternLayout.createDefaultLayout(), 0, true, false, null, 
null, null, configuration)) {
+            // trigger rollover
+            fileManager.initialize();
+            fileManager.rollover();
+        }
+    }
+
+    /**
+     * Tests LOG4J2-1474 CronTriggeringPolicy raise exception and fail to 
rollover log file when evaluateOnStartup is
+     * true.
+     */
+    @Test
+    public void testFactoryMethodOnce() {
+        testFactoryMethod();
+    }
+
+    @Test
+    public void testFactoryMethodSequence() {
+        testFactoryMethod();
+        testFactoryMethod();
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/OnStartupTriggeringPolicyTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/OnStartupTriggeringPolicyTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/OnStartupTriggeringPolicyTest.java
index 6cf8474..f05e4e2 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/OnStartupTriggeringPolicyTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/OnStartupTriggeringPolicyTest.java
@@ -78,7 +78,7 @@ public class OnStartupTriggeringPolicyTest {
                 configuration);
         final OnStartupTriggeringPolicy policy = 
OnStartupTriggeringPolicy.createPolicy(1);
         try (final RollingFileManager manager = 
RollingFileManager.getFileManager(TARGET_FILE, TARGET_PATTERN, true, false,
-                policy, strategy, null, layout, 8192, true, false, 
configuration)) {
+                policy, strategy, null, layout, 8192, true, false, null, null, 
null, configuration)) {
             manager.initialize();
             final String files = Arrays.toString(new 
File(TARGET_FOLDER).listFiles());
             assertTrue(target.toString() + ", files = " + files, 
Files.exists(target));

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerTest.java
index 67abb55..8fa07b9 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerTest.java
@@ -17,6 +17,17 @@
 
 package org.apache.logging.log4j.core.appender.rolling;
 
+import static org.apache.logging.log4j.hamcrest.FileMatchers.beforeNow;
+import static org.apache.logging.log4j.hamcrest.FileMatchers.hasLength;
+import static org.apache.logging.log4j.hamcrest.FileMatchers.isEmpty;
+import static org.apache.logging.log4j.hamcrest.FileMatchers.lastModified;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -29,14 +40,6 @@ import org.apache.logging.log4j.core.util.NullOutputStream;
 import org.apache.logging.log4j.util.Strings;
 import org.junit.Test;
 
-import static org.apache.logging.log4j.hamcrest.FileMatchers.beforeNow;
-import static org.apache.logging.log4j.hamcrest.FileMatchers.hasLength;
-import static org.apache.logging.log4j.hamcrest.FileMatchers.isEmpty;
-import static org.apache.logging.log4j.hamcrest.FileMatchers.lastModified;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.lessThanOrEqualTo;
-import static org.junit.Assert.*;
-
 /**
  * Tests the RollingRandomAccessFileManager class.
  */
@@ -60,7 +63,8 @@ public class RollingRandomAccessFileManagerTest {
             final RolloverStrategy rolloverStrategy = null;
             final RollingRandomAccessFileManager manager = new 
RollingRandomAccessFileManager(null, raf,
                     file.getName(), Strings.EMPTY, os, append, flushNow,
-                    RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE, 
triggerSize, time, triggerPolicy, rolloverStrategy, null, null, true);
+                    RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE, 
triggerSize, time, triggerPolicy, rolloverStrategy,
+                    null, null, null, null, null, true);
 
             final int size = 
RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE * 3;
             final byte[] data = new byte[size];
@@ -89,7 +93,8 @@ public class RollingRandomAccessFileManagerTest {
             final RolloverStrategy rolloverStrategy = null;
             final RollingRandomAccessFileManager manager = new 
RollingRandomAccessFileManager(null, raf,
                     file.getName(), Strings.EMPTY, os, append, flushNow,
-                    RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE, 
triggerSize, time, triggerPolicy, rolloverStrategy, null, null, true);
+                    RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE, 
triggerSize, time, triggerPolicy, rolloverStrategy,
+                    null, null, null, null, null, true);
 
             final int size = 
RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE * 3 + 1;
             final byte[] data = new byte[size];
@@ -117,7 +122,7 @@ public class RollingRandomAccessFileManagerTest {
             final RolloverStrategy rolloverStrategy = null;
             final RollingRandomAccessFileManager manager = new 
RollingRandomAccessFileManager(null, raf,
                     file.getName(), Strings.EMPTY, os, append, flushNow, 
bufferSize, triggerSize, time, triggerPolicy,
-                    rolloverStrategy, null, null, true);
+                    rolloverStrategy, null, null, null, null, null, true);
 
             // check the resulting buffer size is what was requested
             assertEquals(bufferSize, manager.getBufferSize());
@@ -149,7 +154,7 @@ public class RollingRandomAccessFileManagerTest {
                 //
                 file.getAbsolutePath(), Strings.EMPTY, isAppend, 
immediateFlush,
                 RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE, new 
SizeBasedTriggeringPolicy(Long.MAX_VALUE), //
-                null, null, null, null);
+                null, null, null, null, null, null, null);
         manager.write(bytes, 0, bytes.length, immediateFlush);
         final int expected = bytes.length * 2;
         assertThat("appended, not overwritten", file, hasLength(expected));
@@ -171,7 +176,7 @@ public class RollingRandomAccessFileManagerTest {
                 //
                 file.getAbsolutePath(), Strings.EMPTY, isAppend, true,
                 RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE, new 
SizeBasedTriggeringPolicy(Long.MAX_VALUE), //
-                null, null, null, null);
+                null, null, null, null, null, null, null);
         assertTrue(manager.getFileTime() < expectedMax);
         assertTrue(manager.getFileTime() >= expectedMin);
     }
@@ -189,7 +194,7 @@ public class RollingRandomAccessFileManagerTest {
                 //
                 file.getAbsolutePath(), Strings.EMPTY, isAppend, true,
                 RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE, new 
SizeBasedTriggeringPolicy(Long.MAX_VALUE), //
-                null, null, null, null);
+                null, null, null, null, null, null, null);
         assertThat(file, lastModified(equalTo(manager.getFileTime())));
     }
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 808fd15..b592d4b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -281,7 +281,7 @@
       <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-lang3</artifactId>
-        <version>3.5</version>
+        <version>3.6</version>
       </dependency>
       <dependency>
         <groupId>com.beust</groupId>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8318a385/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 21f9b31..2f072c1 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -31,6 +31,9 @@
          - "remove" - Removed
     -->
     <release version="2.9.0" date="2017-MM-DD" description="GA Release 2.9.0">
+      <action issue="LOG4J2-1699" dev="ggregory" type="add" due-to="Demetrios 
Dimatos, Pierrick HYMBERT">
+        Configurable Log File Permissions with PosixFilePermission.
+      </action>
       <action issue="LOG4J2-1945" dev="ggregory" type="add">
         Generate source jas for all test jars.
       </action>

Reply via email to