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