001/* MemoryHandler.java -- a class for buffering log messages in a memory buffer
002   Copyright (C) 2002, 2004 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package java.util.logging;
039
040/**
041 * A <code>MemoryHandler</code> maintains a circular buffer of
042 * log records.
043 *
044 * <p><strong>Configuration:</strong> Values of the subsequent
045 * <code>LogManager</code> properties are taken into consideration
046 * when a <code>MemoryHandler</code> is initialized.
047 * If a property is not defined, or if it has an invalid
048 * value, a default is taken without an exception being thrown.
049 *
050 * <ul>
051 * <li><code>java.util.MemoryHandler.level</code> - specifies
052 *     the initial severity level threshold. Default value:
053 *     <code>Level.ALL</code>.</li>
054 * <li><code>java.util.MemoryHandler.filter</code> - specifies
055 *     the name of a Filter class. Default value: No Filter.</li>
056 * <li><code>java.util.MemoryHandler.size</code> - specifies the
057 *     maximum number of log records that are kept in the circular
058 *     buffer.  Default value: 1000.</li>
059 * <li><code>java.util.MemoryHandler.push</code> - specifies the
060 *     <code>pushLevel</code>. Default value:
061 *     <code>Level.SEVERE</code>.</li>
062 * <li><code>java.util.MemoryHandler.target</code> - specifies the
063 *     name of a subclass of {@link Handler} that will be used as the
064 *     target handler.  There is no default value for this property;
065 *     if it is not set, the no-argument MemoryHandler constructor
066 *     will throw an exception.</li>
067 * </ul>
068 *
069 * @author Sascha Brawer (brawer@acm.org)
070 */
071public class MemoryHandler
072  extends Handler
073{
074  /**
075   * The storage area used for buffering the unpushed log records in
076   * memory.
077   */
078  private final LogRecord[] buffer;
079
080
081  /**
082   * The current position in the circular buffer. For a new
083   * MemoryHandler, or immediately after {@link #push()} was called,
084   * the value of this variable is zero.  Each call to {@link
085   * #publish(LogRecord)} will store the published LogRecord into
086   * <code>buffer[position]</code> before position is incremented by
087   * one.  If position becomes greater than the size of the buffer, it
088   * is reset to zero.
089   */
090  private int position;
091
092
093  /**
094   * The number of log records which have been published, but not
095   * pushed yet to the target handler.
096   */
097  private int numPublished;
098
099
100  /**
101   * The push level threshold for this <code>Handler</code>.  When a
102   * record is published whose severity level is greater than or equal
103   * to the <code>pushLevel</code> of this <code>MemoryHandler</code>,
104   * the {@link #push()} method will be invoked for pushing the buffer
105   * contents to the target <code>Handler</code>.
106   */
107  private Level pushLevel;
108
109
110  /**
111   * The Handler to which log records are forwarded for actual
112   * publication.
113   */
114  private final Handler target;
115
116
117  /**
118   * Constructs a <code>MemoryHandler</code> for keeping a circular
119   * buffer of LogRecords; the initial configuration is determined by
120   * the <code>LogManager</code> properties described above.
121   */
122  public MemoryHandler()
123  {
124    this((Handler) LogManager.getInstanceProperty(
125           "java.util.logging.MemoryHandler.target",
126           Handler.class, /* default */ null),
127         LogManager.getIntPropertyClamped(
128           "java.util.logging.MemoryHandler.size",
129           /* default */ 1000,
130           /* minimum value */ 1,
131           /* maximum value */ Integer.MAX_VALUE),
132         LogManager.getLevelProperty(
133           "java.util.logging.MemoryHandler.push",
134           /* default push level */ Level.SEVERE));
135  }
136
137
138  /**
139   * Constructs a <code>MemoryHandler</code> for keeping a circular
140   * buffer of LogRecords, given some parameters. The values of the
141   * other parameters are taken from LogManager properties, as
142   * described above.
143   *
144   * @param target the target handler that will receive those
145   *               log records that are passed on for publication.
146   *
147   * @param size the number of log records that are kept in the buffer.
148   *             The value must be a at least one.
149   *
150   * @param pushLevel the push level threshold for this
151   *     <code>MemoryHandler</code>.  When a record is published whose
152   *     severity level is greater than or equal to
153   *     <code>pushLevel</code>, the {@link #push()} method will be
154   *     invoked in order to push the bufffer contents to
155   *     <code>target</code>.
156   *
157   * @throws java.lang.IllegalArgumentException if <code>size</code>
158   *         is negative or zero. The GNU implementation also throws
159   *         an IllegalArgumentException if <code>target</code> or
160   *         <code>pushLevel</code> are <code>null</code>, but the
161   *         API specification does not prescribe what should happen
162   *         in those cases.
163   */
164  public MemoryHandler(Handler target, int size, Level pushLevel)
165  {
166    if ((target == null) || (size <= 0) || (pushLevel == null))
167      throw new IllegalArgumentException();
168
169    buffer = new LogRecord[size];
170    this.pushLevel = pushLevel;
171    this.target = target;
172
173    setLevel(LogManager.getLevelProperty(
174      "java.util.logging.MemoryHandler.level",
175      /* default value */ Level.ALL));
176
177    setFilter((Filter) LogManager.getInstanceProperty(
178      "java.util.logging.MemoryHandler.filter",
179      /* must be instance of */ Filter.class,
180      /* default value */ null));
181  }
182
183
184  /**
185   * Stores a <code>LogRecord</code> in a fixed-size circular buffer,
186   * provided the record passes all tests for being loggable.  If the
187   * buffer is full, the oldest record will be discarded.
188   *
189   * <p>If the record has a severity level which is greater than or
190   * equal to the <code>pushLevel</code> of this
191   * <code>MemoryHandler</code>, the {@link #push()} method will be
192   * invoked for pushing the buffer contents to the target
193   * <code>Handler</code>.
194   *
195   * <p>Most applications do not need to call this method directly.
196   * Instead, they will use use a {@link Logger}, which will create
197   * LogRecords and distribute them to registered handlers.
198   *
199   * @param record the log event to be published.
200   */
201  public void publish(LogRecord record)
202  {
203    if (!isLoggable(record))
204      return;
205
206    buffer[position] = record;
207    position = (position + 1) % buffer.length;
208    numPublished = numPublished + 1;
209
210    if (record.getLevel().intValue() >= pushLevel.intValue())
211      push();
212  }
213
214
215  /**
216   * Pushes the contents of the memory buffer to the target
217   * <code>Handler</code> and clears the buffer. Note that
218   * the target handler will discard those records that do
219   * not satisfy its own severity level threshold, or that are
220   * not considered loggable by an installed {@link Filter}.
221   *
222   * <p>In case of an I/O failure, the {@link ErrorManager} of the
223   * target <code>Handler</code> will be notified, but the caller of
224   * this method will not receive an exception.
225   */
226  public void push()
227  {
228    int i;
229
230    if (numPublished < buffer.length)
231    {
232      for (i = 0; i < position; i++)
233        target.publish(buffer[i]);
234    }
235    else
236    {
237      for (i = position; i < buffer.length; i++)
238        target.publish(buffer[i]);
239      for (i = 0; i < position; i++)
240        target.publish(buffer[i]);
241    }
242
243    numPublished = 0;
244    position = 0;
245  }
246
247
248  /**
249   * Forces any data that may have been buffered by the target
250   * <code>Handler</code> to the underlying output device, but
251   * does <em>not</em> push the contents of the circular memory
252   * buffer to the target handler.
253   *
254   * <p>In case of an I/O failure, the {@link ErrorManager} of the
255   * target <code>Handler</code> will be notified, but the caller of
256   * this method will not receive an exception.
257   *
258   * @see #push()
259   */
260  public void flush()
261  {
262    target.flush();
263  }
264
265
266  /**
267   * Closes this <code>MemoryHandler</code> and its associated target
268   * handler, discarding the contents of the memory buffer.  However,
269   * any data that may have been buffered by the target
270   * <code>Handler</code> is forced to the underlying output device.
271   *
272   * <p>As soon as <code>close</code> has been called,
273   * a <code>Handler</code> should not be used anymore. Attempts
274   * to publish log records, to flush buffers, or to modify the
275   * <code>Handler</code> in any other way may throw runtime
276   * exceptions after calling <code>close</code>.</p>
277   *
278   * <p>In case of an I/O failure, the <code>ErrorManager</code> of
279   * the associated target <code>Handler</code> will be informed, but
280   * the caller of this method will not receive an exception.</p>
281   *
282   * @throws SecurityException if a security manager exists and
283   *         the caller is not granted the permission to control
284   *         the logging infrastructure.
285   *
286   * @see #push()
287   */
288  public void close()
289    throws SecurityException
290  {
291    push();
292
293    /* This will check for LoggingPermission("control"). If the
294     * current security context does not grant this permission,
295     * push() has been executed, but this does not impose a
296     * security risk.
297     */
298    target.close();
299  }
300
301
302
303  /**
304   * Returns the push level threshold for this <code>Handler</code>.
305   * When a record is published whose severity level is greater
306   * than or equal to the <code>pushLevel</code> of this
307   * <code>MemoryHandler</code>, the {@link #push()} method will be
308   * invoked for pushing the buffer contents to the target
309   * <code>Handler</code>.
310   *
311   * @return the push level threshold for automatic pushing.
312   */
313  public Level getPushLevel()
314  {
315    return pushLevel;
316  }
317
318
319  /**
320   * Sets the push level threshold for this <code>Handler</code>.
321   * When a record is published whose severity level is greater
322   * than or equal to the <code>pushLevel</code> of this
323   * <code>MemoryHandler</code>, the {@link #push()} method will be
324   * invoked for pushing the buffer contents to the target
325   * <code>Handler</code>.
326   *
327   * @param pushLevel the push level threshold for automatic pushing.
328   *
329   * @exception SecurityException if a security manager exists and
330   *            the caller is not granted the permission to control
331   *            the logging infrastructure.
332   *
333   * @exception NullPointerException if <code>pushLevel</code> is
334   *            <code>null</code>.
335   */
336  public void setPushLevel(Level pushLevel)
337  {
338    LogManager.getLogManager().checkAccess();
339
340    /* Throws a NullPointerException if pushLevel is null. */
341    pushLevel.getClass();
342
343    this.pushLevel = pushLevel;
344  }
345}