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.io.OutputStream;
021: import java.io.OutputStreamWriter;
022: import java.io.UnsupportedEncodingException;
023: import java.io.Writer;
024:
025: import org.apache.harmony.logging.internal.nls.Messages;
026:
027: /**
028: * A <code>StreamHandler</code> object writes log messages to an output
029: * stream, that is, an object of type <code>java.io.OutputStream</code>.
030: * <p>
031: * A <code>StreamHandler</code> reads the following properties from the log
032: * manager to initialize itself:
033: * <ul>
034: * <li>java.util.logging.StreamHandler.encoding - the name of the character set
035: * encoding. Default is the encoding used by the current platform.</li>
036: * <li>java.util.logging.StreamHandler.filter - the name of the
037: * <code>Filter</code> class. No <code>Filter</code> is used by default.</li>
038: * <li>java.util.logging.StreamHandler.formatter - the name of the
039: * <code>Formatter</code> class. Default is
040: * <code>java.util.logging.SimpleFormatter</code>.</li>
041: * <li>java.util.logging.StreamHandler.level - the log level for this
042: * <code>Handler</code>. Default is <code>Level.INFO</code>.</li>
043: * </ul>
044: * </p>
045: * <p>
046: * This class is not thread-safe.
047: * </p>
048: */
049: public class StreamHandler extends Handler {
050:
051: // the output stream this handler writes to
052: private OutputStream os;
053:
054: // the writer that writes to the output stream
055: private Writer writer;
056:
057: // the flag indicating whether the writer has been initialized
058: private boolean writerNotInitialized;
059:
060: /**
061: * Constructs a <code>StreamHandler</code> object. The new stream handler
062: * does not have an associated output stream.
063: */
064: public StreamHandler() {
065: initProperties(
066: "INFO", null, "java.util.logging.SimpleFormatter", //$NON-NLS-1$//$NON-NLS-2$
067: null);
068: this .os = null;
069: this .writer = null;
070: this .writerNotInitialized = true;
071: }
072:
073: /**
074: * Constructs a <code>StreamHandler</code> object with the supplied output
075: * stream. Default properties are read.
076: *
077: * @param os
078: * the output stream this handler writes to
079: */
080: StreamHandler(OutputStream os) {
081: this ();
082: this .os = os;
083: }
084:
085: /**
086: * Constructs a <code>StreamHandler</code> object. Specified default
087: * values will be used if the corresponding properties are found in log
088: * manager's properties.
089: */
090: StreamHandler(String defaultLevel, String defaultFilter,
091: String defaultFormatter, String defaultEncoding) {
092: initProperties(defaultLevel, defaultFilter, defaultFormatter,
093: defaultEncoding);
094: this .os = null;
095: this .writer = null;
096: this .writerNotInitialized = true;
097: }
098:
099: /**
100: * Constructs a <code>StreamHandler</code> object with the supplied output
101: * stream and formatter.
102: *
103: * @param os
104: * the output stream this handler writes to
105: * @param formatter
106: * the formatter this handler uses to format the output
107: */
108: public StreamHandler(OutputStream os, Formatter formatter) {
109: this ();
110: if (os == null) {
111: // logging.2=The OutputStream parameter is null
112: throw new NullPointerException(Messages
113: .getString("logging.2")); //$NON-NLS-1$
114: }
115: if (formatter == null) {
116: // logging.3=The Formatter parameter is null.
117: throw new NullPointerException(Messages
118: .getString("logging.3")); //$NON-NLS-1$
119: }
120: this .os = os;
121: internalSetFormatter(formatter);
122: }
123:
124: // initialize the writer
125: private void initializeWritter() {
126: this .writerNotInitialized = false;
127: if (null == getEncoding()) {
128: this .writer = new OutputStreamWriter(this .os);
129: } else {
130: try {
131: this .writer = new OutputStreamWriter(this .os,
132: getEncoding());
133: } catch (UnsupportedEncodingException e) {
134: /*
135: * Should not happen because it's checked in
136: * super.initProperties().
137: */
138: }
139: }
140: write(getFormatter().getHead(this ));
141: }
142:
143: // Write a string to the output stream.
144: private void write(String s) {
145: try {
146: this .writer.write(s);
147: } catch (Exception e) {
148: // logging.14=Exception occurred when writing to the output stream.
149: getErrorManager().error(
150: Messages.getString("logging.14"), e, //$NON-NLS-1$
151: ErrorManager.WRITE_FAILURE);
152: }
153: }
154:
155: /**
156: * Sets the output stream this handler writes to. Note it does nothing else.
157: *
158: * @param newOs
159: * the new output stream
160: */
161: void internalSetOutputStream(OutputStream newOs) {
162: this .os = newOs;
163: }
164:
165: /**
166: * Sets the output stream this handler writes to. If there's an existing
167: * output stream, the tail string of the associated formatter will be
168: * written to it. Then it will be flushed and closed.
169: *
170: * @param os
171: * the new output stream
172: * @throws SecurityException
173: * If a security manager determines that the caller does not
174: * have the required permission.
175: */
176: protected void setOutputStream(OutputStream os) {
177: if (null == os) {
178: throw new NullPointerException();
179: }
180: LogManager.getLogManager().checkAccess();
181: close(true);
182: this .writer = null;
183: this .os = os;
184: this .writerNotInitialized = true;
185: }
186:
187: /**
188: * Sets the character encoding used by this handler. A <code>null</code>
189: * value indicates the using of the default encoding.
190: *
191: * @param encoding
192: * the character encoding to set
193: * @throws SecurityException
194: * If a security manager determines that the caller does not
195: * have the required permission.
196: * @throws UnsupportedEncodingException
197: * If the specified encoding is not supported by the runtime.
198: */
199: @Override
200: public void setEncoding(String encoding) throws SecurityException,
201: UnsupportedEncodingException {
202: // flush first before set new encoding
203: this .flush();
204: super .setEncoding(encoding);
205: // renew writer only if the writer exists
206: if (null != this .writer) {
207: if (null == getEncoding()) {
208: this .writer = new OutputStreamWriter(this .os);
209: } else {
210: try {
211: this .writer = new OutputStreamWriter(this .os,
212: getEncoding());
213: } catch (UnsupportedEncodingException e) {
214: /*
215: * Should not happen because it's checked in
216: * super.initProperties().
217: */
218: throw new AssertionError(e);
219: }
220: }
221: }
222: }
223:
224: /**
225: * Closes this handler, but the underlying output stream is only closed when
226: * <code>closeStream</code> is <code>true</code>. Security is not
227: * checked.
228: *
229: * @param closeStream
230: * whether to close the underlying output stream
231: */
232: void close(boolean closeStream) {
233: if (null != this .os) {
234: if (this .writerNotInitialized) {
235: initializeWritter();
236: }
237: write(getFormatter().getTail(this ));
238: try {
239: this .writer.flush();
240: if (closeStream) {
241: this .writer.close();
242: this .writer = null;
243: this .os = null;
244: }
245: } catch (Exception e) {
246: // logging.15=Exception occurred when closing the output stream.
247: getErrorManager().error(
248: Messages.getString("logging.15"), e, //$NON-NLS-1$
249: ErrorManager.CLOSE_FAILURE);
250: }
251: }
252: }
253:
254: /**
255: * Closes this handler. The tail string of the formatter associated with
256: * this handler will be written out. A flush operation a subsequent close
257: * operation will then be performed upon the outputstream. Client
258: * applications should not use a handler after closing it.
259: *
260: * @throws SecurityException
261: * If a security manager determines that the caller does not
262: * have the required permission.
263: */
264: @Override
265: public void close() {
266: LogManager.getLogManager().checkAccess();
267: close(true);
268: }
269:
270: /**
271: * Flushes any buffered output.
272: */
273: @Override
274: public void flush() {
275: if (null != this .os) {
276: try {
277: if (null != this .writer) {
278: this .writer.flush();
279: } else {
280: this .os.flush();
281: }
282: } catch (Exception e) {
283: // logging.16=Exception occurred while flushing the output
284: // stream.
285: getErrorManager().error(
286: Messages.getString("logging.16"), //$NON-NLS-1$
287: e, ErrorManager.FLUSH_FAILURE);
288: }
289: }
290: }
291:
292: /**
293: * Accepts an actual logging request. The log record will be formatted and
294: * written to the output stream if the following three conditions are met:
295: * <ul>
296: * <li>the supplied log record has at least the required logging level;
297: * <li>the supplied log record passes the filter associated with this
298: * handler if any;
299: * <li>the output stream associated with this handler is not
300: * <code>null</code>.
301: * </ul>
302: * If it is the first time a log record need to be written out, the head
303: * string of the formatter associated with this handler will be written out
304: * first.
305: *
306: * @param record
307: * the log record to be logged
308: */
309: @Override
310: public synchronized void publish(LogRecord record) {
311: try {
312: if (this .isLoggable(record)) {
313: if (this .writerNotInitialized) {
314: initializeWritter();
315: }
316: String msg = null;
317: try {
318: msg = getFormatter().format(record);
319: } catch (Exception e) {
320: // logging.17=Exception occurred while formatting the log
321: // record.
322: getErrorManager().error(
323: Messages.getString("logging.17"), //$NON-NLS-1$
324: e, ErrorManager.FORMAT_FAILURE);
325: }
326: write(msg);
327: }
328: } catch (Exception e) {
329: // logging.18=Exception occurred while logging the record.
330: getErrorManager().error(
331: Messages.getString("logging.18"), e, //$NON-NLS-1$
332: ErrorManager.GENERIC_FAILURE);
333: }
334: }
335:
336: /**
337: * Determines whether the supplied log record need to be logged. The logging
338: * levels will be checked as well as the filter. The output stream of this
339: * handler is also checked. If it's null, this method returns false.
340: *
341: * @param record
342: * the log record to be checked
343: * @return <code>true</code> if the supplied log record need to be logged,
344: * otherwise <code>false</code>
345: */
346: @Override
347: public boolean isLoggable(LogRecord record) {
348: if (null == record) {
349: return false;
350: }
351: if (null != this .os && super .isLoggable(record)) {
352: return true;
353: }
354: return false;
355: }
356: }
|