001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.io.log.log4j.appender;
020
021import java.io.BufferedWriter;
022import java.io.IOException;
023import java.io.Writer;
024import java.nio.charset.Charset;
025
026import lucee.commons.io.res.Resource;
027import lucee.commons.io.retirement.RetireListener;
028import lucee.commons.io.retirement.RetireOutputStream;
029
030import org.apache.log4j.Layout;
031import org.apache.log4j.RollingFileAppender;
032import org.apache.log4j.WriterAppender;
033import org.apache.log4j.helpers.LogLog;
034import org.apache.log4j.helpers.QuietWriter;
035
036public class ResourceAppender extends WriterAppender implements AppenderState {
037
038  private static final int DEFAULT_BUFFER_SIZE = 8*1024; 
039
040/** Controls file truncatation. The default value for this variable
041   * is <code>true</code>, meaning that by default a
042   * <code>FileAppender</code> will append to an existing file and not
043   * truncate it.
044   *
045   * <p>This option is meaningful only if the FileAppender opens the
046   * file.
047   */
048  protected final boolean fileAppend;
049
050  /**
051     The name of the log file. */
052  protected final Resource res;
053
054  /**
055     Do we do bufferedIO? */
056  protected final boolean bufferedIO;
057
058  /**
059   * Determines the size of IO buffer be. Default is 8K. 
060   */
061  protected final int bufferSize;
062
063  private final int timeout;
064
065private final RetireListener listener; 
066
067  /**
068     Instantiate a FileAppender and open the file designated by
069    <code>filename</code>. The opened filename will become the output
070    destination for this appender.
071
072    <p>The file will be appended to.  */
073  public ResourceAppender(Layout layout, Resource res,Charset charset,RetireListener listener) throws IOException {
074    this(layout, res,charset, true,false,60/* a minute */,DEFAULT_BUFFER_SIZE,listener);
075  }
076
077
078  /**
079    Instantiate a FileAppender and open the file designated by
080    <code>filename</code>. The opened filename will become the output
081    destination for this appender.
082
083    <p>If the <code>append</code> parameter is true, the file will be
084    appended to. Otherwise, the file designated by
085    <code>filename</code> will be truncated before being opened.
086  */
087  public ResourceAppender(Layout layout, Resource res,Charset charset, boolean append,RetireListener listener) throws IOException {
088          this(layout,res,charset,append,false,60/* a minute */,DEFAULT_BUFFER_SIZE,listener);
089  }
090  
091
092  public ResourceAppender(Layout layout, Resource res,Charset charset, boolean append, int timeout,RetireListener listener) throws IOException {
093          this(layout,res,charset,append,false,timeout,DEFAULT_BUFFER_SIZE,listener);
094  }
095
096  /**
097    Instantiate a <code>FileAppender</code> and open the file
098    designated by <code>filename</code>. The opened filename will
099    become the output destination for this appender.
100
101    <p>If the <code>append</code> parameter is true, the file will be
102    appended to. Otherwise, the file designated by
103    <code>filename</code> will be truncated before being opened.
104
105    <p>If the <code>bufferedIO</code> parameter is <code>true</code>,
106    then buffered IO will be used to write to the output file.
107
108  */
109  public ResourceAppender(Layout layout, Resource res,Charset charset, boolean append, boolean bufferedIO,
110        int timeout,int bufferSize,RetireListener listener) throws IOException {
111    this.layout = layout;
112    this.bufferedIO=bufferedIO;
113    this.bufferSize=bufferSize;
114    this.timeout=timeout;
115    this.fileAppend=append;
116    this.res=res;
117    this.listener=listener;
118    setEncoding(charset.name());
119    this.setFile(append);
120  }
121
122  /**
123      Returns the value of the <b>Append</b> option.
124   */
125  public boolean getAppend() {
126    return fileAppend;
127  }
128
129
130  /** Returns the value of the <b>File</b> option. */
131  public Resource getResource() {
132    return res;
133  }
134
135  /**
136     If the value of <b>File</b> is not <code>null</code>, then {@link
137     #setFile} is called with the values of <b>File</b>  and
138     <b>Append</b> properties.
139
140     @since 0.8.1 */
141
142
143 /**
144     Closes the previously opened file.
145  */
146  protected
147  void closeFile() {
148    if(this.qw != null) {
149      try {
150        this.qw.close();
151      }
152      catch(java.io.IOException e) {
153        // Exceptionally, it does not make sense to delegate to an
154        // ErrorHandler. Since a closed appender is basically dead.
155        LogLog.error("Could not close " + qw, e);
156      }
157    }
158  }
159
160  /**
161     Get the value of the <b>BufferedIO</b> option.
162
163     <p>BufferedIO will significatnly increase performance on heavily
164     loaded systems.
165
166  */
167  public
168  boolean getBufferedIO() {
169    return this.bufferedIO;
170  }
171
172
173  /**
174     Get the size of the IO buffer.
175  */
176  public
177  int getBufferSize() {
178    return this.bufferSize;
179  }
180
181
182 
183
184  /**
185    <p>Sets and <i>opens</i> the file where the log output will
186    go. The specified file must be writable.
187
188    <p>If there was already an opened file, then the previous file
189    is closed first.
190
191    <p><b>Do not use this method directly. To configure a FileAppender
192    or one of its subclasses, set its properties one by one and then
193    call activateOptions.</b>
194
195    @param fileName The path to the log file.
196    @param append   If true will append to fileName. Otherwise will
197        truncate fileName.  */
198  protected synchronized void setFile(boolean append) throws IOException {
199    LogLog.debug("setFile called: "+res+", "+append);
200
201    // It does not make sense to have immediate flush and bufferedIO.
202    if(bufferedIO) {
203      setImmediateFlush(false);
204    }
205
206    reset();
207    Resource parent = res.getParentResource();
208    if(!parent.exists()) parent.createDirectory(true);
209    boolean writeHeader = !append || res.length()==0;// this must happen before we open the stream
210    Writer fw = createWriter(new RetireOutputStream(res, append, timeout,listener));
211    if(bufferedIO) {
212      fw = new BufferedWriter(fw, bufferSize);
213    }
214    this.setQWForFiles(fw);
215    if(writeHeader) writeHeader();
216    LogLog.debug("setFile ended");
217  }
218
219
220  /**
221     Sets the quiet writer being used.
222
223     This method is overriden by {@link RollingFileAppender}.
224   */
225  protected void setQWForFiles(Writer writer) {
226     this.qw = new QuietWriter(writer, errorHandler);
227  }
228
229
230  /**
231     Close any previously opened file and call the parent's
232     <code>reset</code>.  */
233  protected void reset() {
234    closeFile();
235    super.reset();
236  }
237
238@Override
239public boolean isClosed() {
240        return closed;
241}
242}