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