View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.appender.rolling;
18  
19  import java.io.File;
20  import java.io.FileOutputStream;
21  import java.io.IOException;
22  import java.io.OutputStream;
23  import java.io.Serializable;
24  import java.nio.ByteBuffer;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.attribute.BasicFileAttributes;
28  import java.nio.file.attribute.FileTime;
29  import java.util.Collection;
30  import java.util.Date;
31  import java.util.concurrent.ArrayBlockingQueue;
32  import java.util.concurrent.ExecutorService;
33  import java.util.concurrent.Semaphore;
34  import java.util.concurrent.ThreadPoolExecutor;
35  import java.util.concurrent.TimeUnit;
36  import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
37  
38  import org.apache.logging.log4j.core.Layout;
39  import org.apache.logging.log4j.core.LifeCycle;
40  import org.apache.logging.log4j.core.LifeCycle2;
41  import org.apache.logging.log4j.core.LogEvent;
42  import org.apache.logging.log4j.core.LoggerContext;
43  import org.apache.logging.log4j.core.appender.ConfigurationFactoryData;
44  import org.apache.logging.log4j.core.appender.FileManager;
45  import org.apache.logging.log4j.core.appender.ManagerFactory;
46  import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction;
47  import org.apache.logging.log4j.core.appender.rolling.action.Action;
48  import org.apache.logging.log4j.core.config.Configuration;
49  import org.apache.logging.log4j.core.util.Constants;
50  import org.apache.logging.log4j.core.util.FileUtils;
51  import org.apache.logging.log4j.core.util.Log4jThreadFactory;
52  
53  /**
54   * The Rolling File Manager.
55   */
56  public class RollingFileManager extends FileManager {
57  
58      private static RollingFileManagerFactory factory = new RollingFileManagerFactory();
59      private static final int MAX_TRIES = 3;
60      private static final int MIN_DURATION = 100;
61      private static final FileTime EPOCH = FileTime.fromMillis(0);
62  
63      protected long size;
64      private long initialTime;
65      private volatile PatternProcessor patternProcessor;
66      private final Semaphore semaphore = new Semaphore(1);
67      private final Log4jThreadFactory threadFactory = Log4jThreadFactory.createThreadFactory("RollingFileManager");
68      private volatile TriggeringPolicy triggeringPolicy;
69      private volatile RolloverStrategy rolloverStrategy;
70      private volatile boolean renameEmptyFiles = false;
71      private volatile boolean initialized = false;
72      private volatile String fileName;
73      private final boolean directWrite;
74  
75      /* This executor pool will create a new Thread for every work async action to be performed. Using it allows
76         us to make sure all the Threads are completed when the Manager is stopped. */
77      private final ExecutorService asyncExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0, TimeUnit.MILLISECONDS,
78              new EmptyQueue(), threadFactory);
79  
80      private static final AtomicReferenceFieldUpdater<RollingFileManager, TriggeringPolicy> triggeringPolicyUpdater =
81              AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, TriggeringPolicy.class, "triggeringPolicy");
82  
83      private static final AtomicReferenceFieldUpdater<RollingFileManager, RolloverStrategy> rolloverStrategyUpdater =
84              AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, RolloverStrategy.class, "rolloverStrategy");
85  
86      private static final AtomicReferenceFieldUpdater<RollingFileManager, PatternProcessor> patternProcessorUpdater =
87              AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, PatternProcessor.class, "patternProcessor");
88  
89      @Deprecated
90      protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
91              final boolean append, final long size, final long initialTime, final TriggeringPolicy triggeringPolicy,
92              final RolloverStrategy rolloverStrategy, final String advertiseURI,
93              final Layout<? extends Serializable> layout, final int bufferSize, final boolean writeHeader) {
94          this(fileName, pattern, os, append, size, initialTime, triggeringPolicy, rolloverStrategy, advertiseURI, layout,
95                  writeHeader, ByteBuffer.wrap(new byte[Constants.ENCODER_BYTE_BUFFER_SIZE]));
96      }
97  
98      @Deprecated
99      protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
100             final boolean append, final long size, final long initialTime, final TriggeringPolicy triggeringPolicy,
101             final RolloverStrategy rolloverStrategy, final String advertiseURI,
102             final Layout<? extends Serializable> layout, final boolean writeHeader, final ByteBuffer buffer) {
103         super(fileName != null ? fileName : pattern, os, append, false, advertiseURI, layout, writeHeader,
104 			buffer);
105         this.size = size;
106         this.initialTime = initialTime;
107         this.triggeringPolicy = triggeringPolicy;
108         this.rolloverStrategy = rolloverStrategy;
109         this.patternProcessor = new PatternProcessor(pattern);
110         this.patternProcessor.setPrevFileTime(initialTime);
111         this.fileName = fileName;
112         this.directWrite = rolloverStrategy instanceof DirectWriteRolloverStrategy;
113     }
114 
115     @Deprecated
116     protected RollingFileManager(final LoggerContext loggerContext, final String fileName, final String pattern, final OutputStream os,
117             final boolean append, final boolean createOnDemand, final long size, final long initialTime,
118             final TriggeringPolicy triggeringPolicy, final RolloverStrategy rolloverStrategy,
119             final String advertiseURI, final Layout<? extends Serializable> layout, final boolean writeHeader, final ByteBuffer buffer) {
120         super(loggerContext, fileName != null ? fileName : pattern, os, append, false, createOnDemand,
121 			advertiseURI, layout, writeHeader, buffer);
122         this.size = size;
123         this.initialTime = initialTime;
124         this.triggeringPolicy = triggeringPolicy;
125         this.rolloverStrategy = rolloverStrategy;
126         this.patternProcessor = new PatternProcessor(pattern);
127         this.patternProcessor.setPrevFileTime(initialTime);
128         this.fileName = fileName;
129         this.directWrite = rolloverStrategy instanceof DirectWriteRolloverStrategy;
130     }
131 
132     /**
133      * @since 2.9
134      */
135     protected RollingFileManager(final LoggerContext loggerContext, final String fileName, final String pattern, final OutputStream os,
136             final boolean append, final boolean createOnDemand, final long size, final long initialTime,
137             final TriggeringPolicy triggeringPolicy, final RolloverStrategy rolloverStrategy,
138             final String advertiseURI, final Layout<? extends Serializable> layout,
139             final String filePermissions, final String fileOwner, final String fileGroup,
140             final boolean writeHeader, final ByteBuffer buffer) {
141         super(loggerContext, fileName != null ? fileName : pattern, os, append, false, createOnDemand,
142 			advertiseURI, layout, filePermissions, fileOwner, fileGroup, writeHeader, buffer);
143         this.size = size;
144         this.initialTime = initialTime;
145         this.patternProcessor = new PatternProcessor(pattern);
146         this.patternProcessor.setPrevFileTime(initialTime);
147         this.triggeringPolicy = triggeringPolicy;
148         this.rolloverStrategy = rolloverStrategy;
149         this.fileName = fileName;
150         this.directWrite = rolloverStrategy instanceof DirectFileRolloverStrategy;
151     }
152 
153     public void initialize() {
154 
155         if (!initialized) {
156             LOGGER.debug("Initializing triggering policy {}", triggeringPolicy);
157             initialized = true;
158             triggeringPolicy.initialize(this);
159             if (triggeringPolicy instanceof LifeCycle) {
160                 ((LifeCycle) triggeringPolicy).start();
161             }
162             if (directWrite) {
163                 // LOG4J2-2485: Initialize size from the most recently written file.
164                 File file = new File(getFileName());
165                 if (file.exists()) {
166                     size = file.length();
167                 } else {
168                     ((DirectFileRolloverStrategy) rolloverStrategy).clearCurrentFileName();
169                 }
170             }
171         }
172     }
173 
174     /**
175      * Returns a RollingFileManager.
176      * @param fileName The file name.
177      * @param pattern The pattern for rolling file.
178      * @param append true if the file should be appended to.
179      * @param bufferedIO true if data should be buffered.
180      * @param policy The TriggeringPolicy.
181      * @param strategy The RolloverStrategy.
182      * @param advertiseURI the URI to use when advertising the file
183      * @param layout The Layout.
184      * @param bufferSize buffer size to use if bufferedIO is true
185      * @param immediateFlush flush on every write or not
186      * @param createOnDemand true if you want to lazy-create the file (a.k.a. on-demand.)
187      * @param filePermissions File permissions
188      * @param fileOwner File owner
189      * @param fileGroup File group
190      * @param configuration The configuration.
191      * @return A RollingFileManager.
192      */
193     public static RollingFileManager getFileManager(final String fileName, final String pattern, final boolean append,
194             final boolean bufferedIO, final TriggeringPolicy policy, final RolloverStrategy strategy,
195             final String advertiseURI, final Layout<? extends Serializable> layout, final int bufferSize,
196             final boolean immediateFlush, final boolean createOnDemand,
197             final String filePermissions, final String fileOwner, final String fileGroup,
198             final Configuration configuration) {
199 
200         if (strategy instanceof DirectWriteRolloverStrategy && fileName != null) {
201             LOGGER.error("The fileName attribute must not be specified with the DirectWriteRolloverStrategy");
202             return null;
203         }
204         final String name = fileName == null ? pattern : fileName;
205         return narrow(RollingFileManager.class, getManager(name, new FactoryData(fileName, pattern, append,
206             bufferedIO, policy, strategy, advertiseURI, layout, bufferSize, immediateFlush, createOnDemand,
207             filePermissions, fileOwner, fileGroup, configuration), factory));
208     }
209 
210     /**
211      * Returns the name of the File being managed.
212      * @return The name of the File being managed.
213      */
214     @Override
215     public String getFileName() {
216         if (directWrite) {
217             fileName = ((DirectFileRolloverStrategy) rolloverStrategy).getCurrentFileName(this);
218         }
219         return fileName;
220     }
221 
222     public boolean isDirectWrite() {
223         return directWrite;
224     }
225 
226     public FileExtension getFileExtension() {
227         return patternProcessor.getFileExtension();
228     }
229 
230     // override to make visible for unit tests
231     @Override
232     protected synchronized void write(final byte[] bytes, final int offset, final int length,
233             final boolean immediateFlush) {
234         super.write(bytes, offset, length, immediateFlush);
235     }
236 
237     @Override
238     protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
239         size += length;
240         super.writeToDestination(bytes, offset, length);
241     }
242 
243     public boolean isRenameEmptyFiles() {
244         return renameEmptyFiles;
245     }
246 
247     public void setRenameEmptyFiles(final boolean renameEmptyFiles) {
248         this.renameEmptyFiles = renameEmptyFiles;
249     }
250 
251     /**
252      * Returns the current size of the file.
253      * @return The size of the file in bytes.
254      */
255     public long getFileSize() {
256         return size + byteBuffer.position();
257     }
258 
259     /**
260      * Returns the time the file was created.
261      * @return The time the file was created.
262      */
263     public long getFileTime() {
264         return initialTime;
265     }
266 
267     /**
268      * Determines if a rollover should occur.
269      * @param event The LogEvent.
270      */
271     public synchronized void checkRollover(final LogEvent event) {
272         if (triggeringPolicy.isTriggeringEvent(event)) {
273             rollover();
274         }
275     }
276 
277     @Override
278     public boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
279         LOGGER.debug("Shutting down RollingFileManager {}", getName());
280         boolean stopped = true;
281         if (triggeringPolicy instanceof LifeCycle2) {
282             stopped &= ((LifeCycle2) triggeringPolicy).stop(timeout, timeUnit);
283         } else if (triggeringPolicy instanceof LifeCycle) {
284             ((LifeCycle) triggeringPolicy).stop();
285             stopped &= true;
286         }
287         final boolean status = super.releaseSub(timeout, timeUnit) && stopped;
288         asyncExecutor.shutdown();
289         try {
290             // Allow at least the minimum interval to pass so async actions can complete.
291             final long millis = timeUnit.toMillis(timeout);
292             final long waitInterval = MIN_DURATION < millis ? millis : MIN_DURATION;
293 
294             for (int count = 1; count <= MAX_TRIES && !asyncExecutor.isTerminated(); ++count) {
295                 asyncExecutor.awaitTermination(waitInterval * count, TimeUnit.MILLISECONDS);
296             }
297             if (asyncExecutor.isTerminated()) {
298                 LOGGER.debug("All asynchronous threads have terminated");
299             } else {
300                 asyncExecutor.shutdownNow();
301                 try {
302                     asyncExecutor.awaitTermination(timeout, timeUnit);
303                     if (asyncExecutor.isTerminated()) {
304                         LOGGER.debug("All asynchronous threads have terminated");
305                     } else {
306                         LOGGER.debug("RollingFileManager shutting down but some asynchronous services may not have completed");
307                     }
308                 } catch (final InterruptedException inner) {
309                     LOGGER.warn("RollingFileManager stopped but some asynchronous services may not have completed.");
310                 }
311             }
312         } catch (final InterruptedException ie) {
313             asyncExecutor.shutdownNow();
314             try {
315                 asyncExecutor.awaitTermination(timeout, timeUnit);
316                 if (asyncExecutor.isTerminated()) {
317                     LOGGER.debug("All asynchronous threads have terminated");
318                 }
319             } catch (final InterruptedException inner) {
320                 LOGGER.warn("RollingFileManager stopped but some asynchronous services may not have completed.");
321             }
322             // Preserve interrupt status
323             Thread.currentThread().interrupt();
324         }
325         LOGGER.debug("RollingFileManager shutdown completed with status {}", status);
326         return status;
327     }
328 
329     public synchronized void rollover(Date prevFileTime, Date prevRollTime) {
330 		getPatternProcessor().setPrevFileTime(prevFileTime.getTime());
331 		getPatternProcessor().setCurrentFileTime(prevRollTime.getTime());
332 		rollover();
333 	}
334 
335     public synchronized void rollover() {
336         if (!hasOutputStream() && !isCreateOnDemand()) {
337             return;
338         }
339         if (rollover(rolloverStrategy)) {
340             try {
341                 size = 0;
342                 initialTime = System.currentTimeMillis();
343                 createFileAfterRollover();
344             } catch (final IOException e) {
345                 logError("Failed to create file after rollover", e);
346             }
347         }
348     }
349 
350     protected void createFileAfterRollover() throws IOException  {
351         setOutputStream(createOutputStream());
352     }
353 
354     /**
355      * Returns the pattern processor.
356      * @return The PatternProcessor.
357      */
358     public PatternProcessor getPatternProcessor() {
359         return patternProcessor;
360     }
361 
362     public void setTriggeringPolicy(final TriggeringPolicy triggeringPolicy) {
363         triggeringPolicy.initialize(this);
364         final TriggeringPolicy policy = this.triggeringPolicy;
365         int count = 0;
366         boolean policyUpdated = false;
367         do {
368             ++count;
369         } while (!(policyUpdated = triggeringPolicyUpdater.compareAndSet(this, this.triggeringPolicy, triggeringPolicy))
370                 && count < MAX_TRIES);
371         if (policyUpdated) {
372             if (triggeringPolicy instanceof LifeCycle) {
373                 ((LifeCycle) triggeringPolicy).start();
374             }
375             if (policy instanceof LifeCycle) {
376                 ((LifeCycle) policy).stop();
377             }
378         } else {
379             if (triggeringPolicy instanceof LifeCycle) {
380                 ((LifeCycle) triggeringPolicy).stop();
381             }
382         }
383     }
384 
385     public void setRolloverStrategy(final RolloverStrategy rolloverStrategy) {
386         rolloverStrategyUpdater.compareAndSet(this, this.rolloverStrategy, rolloverStrategy);
387     }
388 
389     public void setPatternProcessor(final PatternProcessor patternProcessor) {
390         patternProcessorUpdater.compareAndSet(this, this.patternProcessor, patternProcessor);
391     }
392 
393     /**
394      * Returns the triggering policy.
395      * @param <T> TriggeringPolicy type
396      * @return The TriggeringPolicy
397      */
398     @SuppressWarnings("unchecked")
399     public <T extends TriggeringPolicy> T getTriggeringPolicy() {
400         // TODO We could parameterize this class with a TriggeringPolicy instead of type casting here.
401         return (T) this.triggeringPolicy;
402     }
403 
404     /**
405      * Returns the rollover strategy.
406      * @return The RolloverStrategy
407      */
408     public RolloverStrategy getRolloverStrategy() {
409         return this.rolloverStrategy;
410     }
411 
412     private boolean rollover(final RolloverStrategy strategy) {
413 
414         boolean releaseRequired = false;
415         try {
416             // Block until the asynchronous operation is completed.
417             semaphore.acquire();
418             releaseRequired = true;
419         } catch (final InterruptedException e) {
420             logError("Thread interrupted while attempting to check rollover", e);
421             return false;
422         }
423 
424         boolean success = true;
425 
426         try {
427             final RolloverDescription descriptor = strategy.rollover(this);
428             if (descriptor != null) {
429                 writeFooter();
430                 closeOutputStream();
431                 if (descriptor.getSynchronous() != null) {
432                     LOGGER.debug("RollingFileManager executing synchronous {}", descriptor.getSynchronous());
433                     try {
434                         success = descriptor.getSynchronous().execute();
435                     } catch (final Exception ex) {
436                         success = false;
437                         logError("Caught error in synchronous task", ex);
438                     }
439                 }
440 
441                 if (success && descriptor.getAsynchronous() != null) {
442                     LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous());
443                     asyncExecutor.execute(new AsyncAction(descriptor.getAsynchronous(), this));
444                     releaseRequired = false;
445                 }
446                 return true;
447             }
448             return false;
449         } finally {
450             if (releaseRequired) {
451                 semaphore.release();
452             }
453         }
454 
455     }
456 
457     /**
458      * Performs actions asynchronously.
459      */
460     private static class AsyncAction extends AbstractAction {
461 
462         private final Action action;
463         private final RollingFileManager manager;
464 
465         /**
466          * Constructor.
467          * @param act The action to perform.
468          * @param manager The manager.
469          */
470         public AsyncAction(final Action act, final RollingFileManager manager) {
471             this.action = act;
472             this.manager = manager;
473         }
474 
475         /**
476          * Executes an action.
477          *
478          * @return true if action was successful.  A return value of false will cause
479          *         the rollover to be aborted if possible.
480          * @throws java.io.IOException if IO error, a thrown exception will cause the rollover
481          *                             to be aborted if possible.
482          */
483         @Override
484         public boolean execute() throws IOException {
485             try {
486                 return action.execute();
487             } finally {
488                 manager.semaphore.release();
489             }
490         }
491 
492         /**
493          * Cancels the action if not already initialized or waits till completion.
494          */
495         @Override
496         public void close() {
497             action.close();
498         }
499 
500         /**
501          * Determines if action has been completed.
502          *
503          * @return true if action is complete.
504          */
505         @Override
506         public boolean isComplete() {
507             return action.isComplete();
508         }
509 
510         @Override
511         public String toString() {
512             final StringBuilder builder = new StringBuilder();
513             builder.append(super.toString());
514             builder.append("[action=");
515             builder.append(action);
516             builder.append(", manager=");
517             builder.append(manager);
518             builder.append(", isComplete()=");
519             builder.append(isComplete());
520             builder.append(", isInterrupted()=");
521             builder.append(isInterrupted());
522             builder.append("]");
523             return builder.toString();
524         }
525     }
526 
527     /**
528      * Factory data.
529      */
530     private static class FactoryData extends ConfigurationFactoryData {
531         private final String fileName;
532         private final String pattern;
533         private final boolean append;
534         private final boolean bufferedIO;
535         private final int bufferSize;
536         private final boolean immediateFlush;
537         private final boolean createOnDemand;
538         private final TriggeringPolicy policy;
539         private final RolloverStrategy strategy;
540         private final String advertiseURI;
541         private final Layout<? extends Serializable> layout;
542         private final String filePermissions;
543         private final String fileOwner;
544         private final String fileGroup;
545 
546         /**
547          * Creates the data for the factory.
548          * @param pattern The pattern.
549          * @param append The append flag.
550          * @param bufferedIO The bufferedIO flag.
551          * @param advertiseURI
552          * @param layout The Layout.
553          * @param bufferSize the buffer size
554          * @param immediateFlush flush on every write or not
555          * @param createOnDemand true if you want to lazy-create the file (a.k.a. on-demand.)
556          * @param filePermissions File permissions
557          * @param fileOwner File owner
558          * @param fileGroup File group
559          * @param configuration The configuration
560          */
561         public FactoryData(final String fileName, final String pattern, final boolean append, final boolean bufferedIO,
562                 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
563                 final Layout<? extends Serializable> layout, final int bufferSize, final boolean immediateFlush,
564                 final boolean createOnDemand, final String filePermissions, final String fileOwner, final String fileGroup,
565                 final Configuration configuration) {
566             super(configuration);
567             this.fileName = fileName;
568             this.pattern = pattern;
569             this.append = append;
570             this.bufferedIO = bufferedIO;
571             this.bufferSize = bufferSize;
572             this.policy = policy;
573             this.strategy = strategy;
574             this.advertiseURI = advertiseURI;
575             this.layout = layout;
576             this.immediateFlush = immediateFlush;
577             this.createOnDemand = createOnDemand;
578             this.filePermissions = filePermissions;
579             this.fileOwner = fileOwner;
580             this.fileGroup = fileGroup;
581         }
582 
583         public TriggeringPolicy getTriggeringPolicy() {
584             return this.policy;
585         }
586 
587         public RolloverStrategy getRolloverStrategy() {
588             return this.strategy;
589         }
590 
591         public String getPattern() {
592             return pattern;
593         }
594 
595         @Override
596         public String toString() {
597             final StringBuilder builder = new StringBuilder();
598             builder.append(super.toString());
599             builder.append("[pattern=");
600             builder.append(pattern);
601             builder.append(", append=");
602             builder.append(append);
603             builder.append(", bufferedIO=");
604             builder.append(bufferedIO);
605             builder.append(", bufferSize=");
606             builder.append(bufferSize);
607             builder.append(", policy=");
608             builder.append(policy);
609             builder.append(", strategy=");
610             builder.append(strategy);
611             builder.append(", advertiseURI=");
612             builder.append(advertiseURI);
613             builder.append(", layout=");
614             builder.append(layout);
615             builder.append(", filePermissions=");
616             builder.append(filePermissions);
617             builder.append(", fileOwner=");
618             builder.append(fileOwner);
619             builder.append("]");
620             return builder.toString();
621         }
622     }
623 
624     /**
625      * Updates the RollingFileManager's data during a reconfiguration. This method should be considered private.
626      * It is not thread safe and calling it outside of a reconfiguration may lead to errors. This method may be
627      * made protected in a future release.
628      * @param data The data to update.
629      */
630     @Override
631     public void updateData(final Object data) {
632         final FactoryData factoryData = (FactoryData) data;
633         setRolloverStrategy(factoryData.getRolloverStrategy());
634         setPatternProcessor(new PatternProcessor(factoryData.getPattern(), getPatternProcessor()));
635         setTriggeringPolicy(factoryData.getTriggeringPolicy());
636     }
637 
638     /**
639      * Factory to create a RollingFileManager.
640      */
641     private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> {
642 
643         /**
644          * Creates a RollingFileManager.
645          * @param name The name of the entity to manage.
646          * @param data The data required to create the entity.
647          * @return a RollingFileManager.
648          */
649         @Override
650         public RollingFileManager createManager(final String name, final FactoryData data) {
651             long size = 0;
652             File file = null;
653             if (data.fileName != null) {
654                 file = new File(data.fileName);
655 
656                 try {
657                     FileUtils.makeParentDirs(file);
658                     final boolean created = data.createOnDemand ? false : file.createNewFile();
659                     LOGGER.trace("New file '{}' created = {}", name, created);
660                 } catch (final IOException ioe) {
661                     LOGGER.error("Unable to create file " + name, ioe);
662                     return null;
663                 }
664                 size = data.append ? file.length() : 0;
665             }
666 
667             try {
668                 final int actualSize = data.bufferedIO ? data.bufferSize : Constants.ENCODER_BYTE_BUFFER_SIZE;
669                 final ByteBuffer buffer = ByteBuffer.wrap(new byte[actualSize]);
670                 final OutputStream os = data.createOnDemand  || data.fileName == null ? null :
671                         new FileOutputStream(data.fileName, data.append);
672                 // LOG4J2-531 create file first so time has valid value.
673                 final long initialTime = file == null || !file.exists() ? 0 : initialFileTime(file);
674                 final boolean writeHeader = file != null && file.exists() && file.length() == 0;
675 
676                 final RollingFileManager rm = new RollingFileManager(data.getLoggerContext(), data.fileName, data.pattern, os,
677                     data.append, data.createOnDemand, size, initialTime, data.policy, data.strategy, data.advertiseURI,
678                     data.layout, data.filePermissions, data.fileOwner, data.fileGroup, writeHeader, buffer);
679                 if (os != null && rm.isAttributeViewEnabled()) {
680                     rm.defineAttributeView(file.toPath());
681                 }
682 
683                 return rm;
684             } catch (final IOException ex) {
685                 LOGGER.error("RollingFileManager (" + name + ") " + ex, ex);
686             }
687             return null;
688         }
689     }
690 
691     private static long initialFileTime(final File file) {
692         final Path path = file.toPath();
693         if (Files.exists(path)) {
694             try {
695                 final BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
696                 final FileTime fileTime = attrs.creationTime();
697                 if (fileTime.compareTo(EPOCH) > 0) {
698                     LOGGER.debug("Returning file creation time for {}", file.getAbsolutePath());
699                     return fileTime.toMillis();
700                 }
701                 LOGGER.info("Unable to obtain file creation time for " + file.getAbsolutePath());
702             } catch (final Exception ex) {
703                 LOGGER.info("Unable to calculate file creation time for " + file.getAbsolutePath() + ": " + ex.getMessage());
704             }
705         }
706         return file.lastModified();
707     }
708 
709     private static class EmptyQueue extends ArrayBlockingQueue<Runnable> {
710 
711         /**
712          *
713          */
714         private static final long serialVersionUID = 1L;
715 
716         EmptyQueue() {
717             super(1);
718         }
719 
720         @Override
721         public int remainingCapacity() {
722             return 0;
723         }
724 
725         @Override
726         public boolean add(final Runnable runnable) {
727             throw new IllegalStateException("Queue is full");
728         }
729 
730         @Override
731         public void put(final Runnable runnable) throws InterruptedException {
732             /* No point in going into a permanent wait */
733             throw new InterruptedException("Unable to insert into queue");
734         }
735 
736         @Override
737         public boolean offer(final Runnable runnable, final long timeout, final TimeUnit timeUnit) throws InterruptedException {
738             Thread.sleep(timeUnit.toMillis(timeout));
739             return false;
740         }
741 
742         @Override
743         public boolean addAll(final Collection<? extends Runnable> collection) {
744             if (collection.size() > 0) {
745                 throw new IllegalArgumentException("Too many items in collection");
746             }
747             return false;
748         }
749 
750     }
751 
752 }