001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.util.logging;
019:
020: import java.security.AccessController;
021: import java.security.PrivilegedExceptionAction;
022:
023: import org.apache.harmony.logging.internal.nls.Messages;
024:
025: /**
026: * <code>MemoryHandler</code> is a <code>Handler</code> that 'remembers' a
027: * finite number of <code>LogRecord</code>s at a time and stores them in a
028: * buffer without formatting them. When the buffer is full this
029: * <code>Handler</code> overwrites the oldest record as each new record is
030: * added.
031: *
032: * <p>
033: * Every <code>MemoryHandler</code> has a target <code>Handler</code>, and
034: * calling the <code>push()</code> method on the <code>MemoryHandler</code>
035: * will output all buffered records to the target <code>Handler</code> After
036: * the push action, the buffer will be cleared.
037: * </p>
038: *
039: * <p>
040: * The push method can be called directly, but will also be called automatically
041: * if a new <code>LogRecord</code> is added that has a level greater than or
042: * equal to than the value defined for the property
043: * java.util.logging.MemoryHandler.push.
044: * </p>
045: *
046: * <p>
047: * <code>MemoryHandler</code> defines the following configuration properties,
048: * which are read by the <code>LogManager</code> on initialization. If the
049: * properties have not been specified then defaults will be used. The properties
050: * and defaults are as follows:
051: * <ul>
052: * <li>java.util.logging.MemoryHandler.filter - the <code>Filter</code> class
053: * name. No <code>Filter</code> is used by default.</li>
054: * <li>java.util.logging.MemoryHandler.level - the log level for this
055: * <code>Handler</code>. Default is <code>Level.ALL</code>.</li>
056: * <li>java.util.logging.MemoryHandler.push - the push level. Default is
057: * <code>Level.SEVERE</code>.</li>
058: * <li>java.util.logging.MemoryHandler.size - the buffer size in number of
059: * <code>LogRecord</code>s. Default is 1000.</li>
060: * <li>java.util.logging.MemoryHandler.target - the class name of the target
061: * <code>Handler</code>. No default value, which means this property must be
062: * specified either by property setting or by constructor.</li>
063: * </ul>
064: * </p>
065: */
066: public class MemoryHandler extends Handler {
067:
068: // default maximum buffered number of LogRecord
069: private static final int DEFAULT_SIZE = 1000;
070:
071: // target handler
072: private Handler target;
073:
074: // buffer size
075: private int size = DEFAULT_SIZE;
076:
077: // push level
078: private Level push = Level.SEVERE;
079:
080: // LogManager instance for convenience
081: private final LogManager manager = LogManager.getLogManager();
082:
083: // buffer
084: private LogRecord[] buffer;
085:
086: // current position in buffer
087: private int cursor;
088:
089: /**
090: * Default constructor, construct and init a <code>MemoryHandler</code>
091: * using <code>LogManager</code> properties or default values
092: */
093: public MemoryHandler() {
094: super ();
095: String className = this .getClass().getName();
096: // init target
097: final String targetName = manager.getProperty(className
098: + ".target"); //$NON-NLS-1$
099: try {
100: Class<?> targetClass = AccessController
101: .doPrivileged(new PrivilegedExceptionAction<Class<?>>() {
102: public Class<?> run() throws Exception {
103: ClassLoader loader = Thread.currentThread()
104: .getContextClassLoader();
105: if (loader == null) {
106: loader = ClassLoader
107: .getSystemClassLoader();
108: }
109: return loader.loadClass(targetName);
110: }
111: });
112: target = (Handler) targetClass.newInstance();
113: } catch (Exception e) {
114: // logging.10=Cannot load target handler:{0}
115: throw new RuntimeException(Messages.getString("logging.10", //$NON-NLS-1$
116: targetName));
117: }
118: // init size
119: String sizeString = manager.getProperty(className + ".size"); //$NON-NLS-1$
120: if (null != sizeString) {
121: try {
122: size = Integer.parseInt(sizeString);
123: if (size <= 0) {
124: size = DEFAULT_SIZE;
125: }
126: } catch (Exception e) {
127: printInvalidPropMessage(
128: className + ".size", sizeString, e); //$NON-NLS-1$
129: }
130: }
131: // init push level
132: String pushName = manager.getProperty(className + ".push"); //$NON-NLS-1$
133: if (null != pushName) {
134: try {
135: push = Level.parse(pushName);
136: } catch (Exception e) {
137: printInvalidPropMessage(
138: className + ".push", pushName, e); //$NON-NLS-1$
139: }
140: }
141: // init other properties which are common for all Handler
142: initProperties(
143: "ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$
144: buffer = new LogRecord[size];
145: }
146:
147: /**
148: * Construct and init a <code>MemoryHandler</code> using given target,
149: * size and push level, other properties using <code>LogManager</code>
150: * properties or default values
151: *
152: * @param target
153: * the given <code>Handler</code> to output
154: * @param size
155: * the maximum number of buffered <code>LogRecord</code>
156: * @param pushLevel
157: * the push level
158: * @throws IllegalArgumentException
159: * if size<=0
160: */
161: public MemoryHandler(Handler target, int size, Level pushLevel) {
162: if (size <= 0) {
163: // logging.11=Size must be positive.
164: throw new IllegalArgumentException(Messages
165: .getString("logging.11")); //$NON-NLS-1$
166: }
167: target.getLevel();
168: pushLevel.intValue();
169: this .target = target;
170: this .size = size;
171: this .push = pushLevel;
172: initProperties(
173: "ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$
174: buffer = new LogRecord[size];
175: }
176:
177: /**
178: * Close this handler and target handler, free all associated resources
179: *
180: * @throws SecurityException
181: * if security manager exists and it determines that caller does
182: * not have the required permissions to control this handler
183: */
184: @Override
185: public void close() {
186: manager.checkAccess();
187: target.close();
188: setLevel(Level.OFF);
189: }
190:
191: /**
192: * Call target handler to flush any buffered output.
193: *
194: * Note that this doesn't cause this <code>MemoryHandler</code> to push.
195: */
196: @Override
197: public void flush() {
198: target.flush();
199: }
200:
201: /**
202: * Put a given <code>LogRecord</code> into internal buffer.
203: *
204: * If given record is not loggable, just return. Otherwise it is stored in
205: * the buffer. Furthermore if the record's level is not less than the push
206: * level, the push action is triggered to output all the buffered records to
207: * the target handler, and the target handler will publish them.
208: *
209: * @param record
210: * the log record.
211: */
212: @Override
213: public synchronized void publish(LogRecord record) {
214: if (!isLoggable(record)) {
215: return;
216: }
217: if (cursor >= size) {
218: cursor = 0;
219: }
220: buffer[cursor++] = record;
221: if (record.getLevel().intValue() >= push.intValue()) {
222: push();
223: }
224: return;
225: }
226:
227: /**
228: * Return the push level.
229: *
230: * @return the push level
231: */
232: public Level getPushLevel() {
233: return push;
234: }
235:
236: /**
237: * Check if given <code>LogRecord</code> would be put into this
238: * <code>MemoryHandler</code>'s internal buffer.
239: * <p>
240: * The given <code>LogRecord</code> is loggable if and only if it has
241: * appropriate level and it pass any associated filter's check.
242: * </p>
243: * <p>
244: * Note that the push level is not used for this check.
245: * </p>
246: *
247: * @param record
248: * the given <code>LogRecord</code>
249: * @return if the given <code>LogRecord</code> should be logged
250: */
251: @Override
252: public boolean isLoggable(LogRecord record) {
253: return super .isLoggable(record);
254: }
255:
256: /**
257: * Triggers a push action to output all buffered records to the target
258: * handler, and the target handler will publish them. Then the buffer is
259: * cleared.
260: */
261: public void push() {
262: for (int i = cursor; i < size; i++) {
263: if (null != buffer[i]) {
264: target.publish(buffer[i]);
265: }
266: buffer[i] = null;
267: }
268: for (int i = 0; i < cursor; i++) {
269: if (null != buffer[i]) {
270: target.publish(buffer[i]);
271: }
272: buffer[i] = null;
273: }
274: cursor = 0;
275: }
276:
277: /**
278: * Set the push level. The push level is used to check the push action
279: * triggering. When a new <code>LogRecord</code> is put into the internal
280: * buffer and its level is not less than the push level, the push action
281: * will be triggered. Note that set new push level won't trigger push
282: * action.
283: *
284: * @param newLevel
285: * the new level to set
286: * @throws SecurityException
287: * if security manager exists and it determines that caller does
288: * not have the required permissions to control this handler
289: */
290: public void setPushLevel(Level newLevel) {
291: manager.checkAccess();
292: newLevel.intValue();
293: this.push = newLevel;
294: }
295: }
|