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 }