1. /*
  2. * @(#)StreamHandler.java 1.18 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util.logging;
  8. import java.io.*;
  9. /**
  10. * Stream based logging <tt>Handler</tt>.
  11. * <p>
  12. * This is primarily intended as a base class or support class to
  13. * be used in implementing other logging <tt>Handlers</tt>.
  14. * <p>
  15. * <tt>LogRecords</tt> are published to a given <tt>java.io.OutputStream</tt>.
  16. * <p>
  17. * <b>Configuration:</b>
  18. * By default each <tt>StreamHandler</tt> is initialized using the following
  19. * <tt>LogManager</tt> configuration properties. If properties are not defined
  20. * (or have invalid values) then the specified default values are used.
  21. * <ul>
  22. * <li> java.util.logging.StreamHandler.level
  23. * specifies the default level for the <tt>Handler</tt>
  24. * (defaults to <tt>Level.INFO</tt>).
  25. * <li> java.util.logging.StreamHandler.filter
  26. * specifies the name of a <tt>Filter</tt> class to use
  27. * (defaults to no <tt>Filter</tt>).
  28. * <li> java.util.logging.StreamHandler.formatter
  29. * specifies the name of a <tt>Formatter</tt> class to use
  30. * (defaults to <tt>java.util.logging.SimpleFormatter</tt>).
  31. * <li> java.util.logging.StreamHandler.encoding
  32. * the name of the character set encoding to use (defaults to
  33. * the default platform encoding).
  34. * </ul>
  35. *
  36. * @version 1.18, 12/19/03
  37. * @since 1.4
  38. */
  39. public class StreamHandler extends Handler {
  40. private LogManager manager = LogManager.getLogManager();
  41. private OutputStream output;
  42. private boolean doneHeader;
  43. private Writer writer;
  44. // Private method to configure a StreamHandler from LogManager
  45. // properties and/or default values as specified in the class
  46. // javadoc.
  47. private void configure() {
  48. LogManager manager = LogManager.getLogManager();
  49. String cname = getClass().getName();
  50. setLevel(manager.getLevelProperty(cname +".level", Level.INFO));
  51. setFilter(manager.getFilterProperty(cname +".filter", null));
  52. setFormatter(manager.getFormatterProperty(cname +".formatter", new SimpleFormatter()));
  53. try {
  54. setEncoding(manager.getStringProperty(cname +".encoding", null));
  55. } catch (Exception ex) {
  56. try {
  57. setEncoding(null);
  58. } catch (Exception ex2) {
  59. // doing a setEncoding with null should always work.
  60. // assert false;
  61. }
  62. }
  63. }
  64. /**
  65. * Create a <tt>StreamHandler</tt>, with no current output stream.
  66. */
  67. public StreamHandler() {
  68. sealed = false;
  69. configure();
  70. sealed = true;
  71. }
  72. /**
  73. * Create a <tt>StreamHandler</tt> with a given <tt>Formatter</tt>
  74. * and output stream.
  75. * <p>
  76. * @param out the target output stream
  77. * @param formatter Formatter to be used to format output
  78. */
  79. public StreamHandler(OutputStream out, Formatter formatter) {
  80. sealed = false;
  81. configure();
  82. setFormatter(formatter);
  83. setOutputStream(out);
  84. sealed = true;
  85. }
  86. /**
  87. * Change the output stream.
  88. * <P>
  89. * If there is a current output stream then the <tt>Formatter</tt>'s
  90. * tail string is written and the stream is flushed and closed.
  91. * Then the output stream is replaced with the new output stream.
  92. *
  93. * @param out New output stream. May not be null.
  94. * @exception SecurityException if a security manager exists and if
  95. * the caller does not have <tt>LoggingPermission("control")</tt>.
  96. */
  97. protected synchronized void setOutputStream(OutputStream out) throws SecurityException {
  98. if (out == null) {
  99. throw new NullPointerException();
  100. }
  101. flushAndClose();
  102. output = out;
  103. doneHeader = false;
  104. String encoding = getEncoding();
  105. if (encoding == null) {
  106. writer = new OutputStreamWriter(output);
  107. } else {
  108. try {
  109. writer = new OutputStreamWriter(output, encoding);
  110. } catch (UnsupportedEncodingException ex) {
  111. // This shouldn't happen. The setEncoding method
  112. // should have validated that the encoding is OK.
  113. throw new Error("Unexpected exception " + ex);
  114. }
  115. }
  116. }
  117. /**
  118. * Set (or change) the character encoding used by this <tt>Handler</tt>.
  119. * <p>
  120. * The encoding should be set before any <tt>LogRecords</tt> are written
  121. * to the <tt>Handler</tt>.
  122. *
  123. * @param encoding The name of a supported character encoding.
  124. * May be null, to indicate the default platform encoding.
  125. * @exception SecurityException if a security manager exists and if
  126. * the caller does not have <tt>LoggingPermission("control")</tt>.
  127. * @exception UnsupportedEncodingException if the named encoding is
  128. * not supported.
  129. */
  130. public void setEncoding(String encoding)
  131. throws SecurityException, java.io.UnsupportedEncodingException {
  132. super.setEncoding(encoding);
  133. if (output == null) {
  134. return;
  135. }
  136. // Replace the current writer with a writer for the new encoding.
  137. flush();
  138. if (encoding == null) {
  139. writer = new OutputStreamWriter(output);
  140. } else {
  141. writer = new OutputStreamWriter(output, encoding);
  142. }
  143. }
  144. /**
  145. * Format and publish a <tt>LogRecord</tt>.
  146. * <p>
  147. * The <tt>StreamHandler</tt> first checks if there is an <tt>OutputStream</tt>
  148. * and if the given <tt>LogRecord</tt> has at least the required log level.
  149. * If not it silently returns. If so, it calls any associated
  150. * <tt>Filter</tt> to check if the record should be published. If so,
  151. * it calls its <tt>Formatter</tt> to format the record and then writes
  152. * the result to the current output stream.
  153. * <p>
  154. * If this is the first <tt>LogRecord</tt> to be written to a given
  155. * <tt>OutputStream</tt>, the <tt>Formatter</tt>'s "head" string is
  156. * written to the stream before the <tt>LogRecord</tt> is written.
  157. *
  158. * @param record description of the log event. A null record is
  159. * silently ignored and is not published
  160. */
  161. public synchronized void publish(LogRecord record) {
  162. if (!isLoggable(record)) {
  163. return;
  164. }
  165. String msg;
  166. try {
  167. msg = getFormatter().format(record);
  168. } catch (Exception ex) {
  169. // We don't want to throw an exception here, but we
  170. // report the exception to any registered ErrorManager.
  171. reportError(null, ex, ErrorManager.FORMAT_FAILURE);
  172. return;
  173. }
  174. try {
  175. if (!doneHeader) {
  176. writer.write(getFormatter().getHead(this));
  177. doneHeader = true;
  178. }
  179. writer.write(msg);
  180. } catch (Exception ex) {
  181. // We don't want to throw an exception here, but we
  182. // report the exception to any registered ErrorManager.
  183. reportError(null, ex, ErrorManager.WRITE_FAILURE);
  184. }
  185. }
  186. /**
  187. * Check if this <tt>Handler</tt> would actually log a given <tt>LogRecord</tt>.
  188. * <p>
  189. * This method checks if the <tt>LogRecord</tt> has an appropriate level and
  190. * whether it satisfies any <tt>Filter</tt>. It will also return false if
  191. * no output stream has been assigned yet or the LogRecord is Null.
  192. * <p>
  193. * @param record a <tt>LogRecord</tt>
  194. * @return true if the <tt>LogRecord</tt> would be logged.
  195. *
  196. */
  197. public boolean isLoggable(LogRecord record) {
  198. if (writer == null || record == null) {
  199. return false;
  200. }
  201. return super.isLoggable(record);
  202. }
  203. /**
  204. * Flush any buffered messages.
  205. */
  206. public synchronized void flush() {
  207. if (writer != null) {
  208. try {
  209. writer.flush();
  210. } catch (Exception ex) {
  211. // We don't want to throw an exception here, but we
  212. // report the exception to any registered ErrorManager.
  213. reportError(null, ex, ErrorManager.FLUSH_FAILURE);
  214. }
  215. }
  216. }
  217. private synchronized void flushAndClose() throws SecurityException {
  218. checkAccess();
  219. if (writer != null) {
  220. try {
  221. if (!doneHeader) {
  222. writer.write(getFormatter().getHead(this));
  223. doneHeader = true;
  224. }
  225. writer.write(getFormatter().getTail(this));
  226. writer.flush();
  227. writer.close();
  228. } catch (Exception ex) {
  229. // We don't want to throw an exception here, but we
  230. // report the exception to any registered ErrorManager.
  231. reportError(null, ex, ErrorManager.CLOSE_FAILURE);
  232. }
  233. writer = null;
  234. output = null;
  235. }
  236. }
  237. /**
  238. * Close the current output stream.
  239. * <p>
  240. * The <tt>Formatter</tt>'s "tail" string is written to the stream before it
  241. * is closed. In addition, if the <tt>Formatter</tt>'s "head" string has not
  242. * yet been written to the stream, it will be written before the
  243. * "tail" string.
  244. *
  245. * @exception SecurityException if a security manager exists and if
  246. * the caller does not have LoggingPermission("control").
  247. * @exception SecurityException if a security manager exists and if
  248. * the caller does not have LoggingPermission("control").
  249. */
  250. public synchronized void close() throws SecurityException {
  251. flushAndClose();
  252. }
  253. }