001 /* MemoryHandler.java -- a class for buffering log messages in a memory buffer
002 Copyright (C) 2002, 2004 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038 package 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 */
071 public 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 }