1 /*
   2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package java.util.logging;
  28 
  29 import java.io.UnsupportedEncodingException;
  30 import java.security.AccessController;
  31 import java.security.PrivilegedAction;
  32 
  33 /**
  34  * A <tt>Handler</tt> object takes log messages from a <tt>Logger</tt> and
  35  * exports them.  It might for example, write them to a console
  36  * or write them to a file, or send them to a network logging service,
  37  * or forward them to an OS log, or whatever.
  38  * <p>
  39  * A <tt>Handler</tt> can be disabled by doing a <tt>setLevel(Level.OFF)</tt>
  40  * and can  be re-enabled by doing a <tt>setLevel</tt> with an appropriate level.
  41  * <p>
  42  * <tt>Handler</tt> classes typically use <tt>LogManager</tt> properties to set
  43  * default values for the <tt>Handler</tt>'s <tt>Filter</tt>, <tt>Formatter</tt>,
  44  * and <tt>Level</tt>.  See the specific documentation for each concrete
  45  * <tt>Handler</tt> class.
  46  *
  47  *
  48  * @since 1.4
  49  */
  50 
  51 public abstract class Handler {
  52     private static final int offValue = Level.OFF.intValue();
  53     private final LogManager manager = LogManager.getLogManager();
  54 
  55     // We're using volatile here to avoid synchronizing getters, which
  56     // would prevent other threads from calling isLoggable()
  57     // while publish() is executing.
  58     // On the other hand, setters will be synchronized to exclude concurrent
  59     // execution with more complex methods, such as StreamHandler.publish().
  60     // We wouldn't want 'level' to be changed by another thread in the middle
  61     // of the execution of a 'publish' call.
  62     private volatile Filter filter;
  63     private volatile Formatter formatter;
  64     private volatile Level logLevel = Level.ALL;
  65     private volatile ErrorManager errorManager = new ErrorManager();
  66     private volatile String encoding;
  67 
  68     /**
  69      * Default constructor.  The resulting <tt>Handler</tt> has a log
  70      * level of <tt>Level.ALL</tt>, no <tt>Formatter</tt>, and no
  71      * <tt>Filter</tt>.  A default <tt>ErrorManager</tt> instance is installed
  72      * as the <tt>ErrorManager</tt>.
  73      */
  74     protected Handler() {
  75     }
  76 
  77     /**
  78      * Package-private constructor for chaining from subclass constructors
  79      * that wish to configure the handler with specific default and/or
  80      * specified values.
  81      *
  82      * @param defaultLevel       a default {@link Level} to configure if one is
  83      *                           not found in LogManager configuration properties
  84      * @param defaultFormatter   a default {@link Formatter} to configure if one is
  85      *                           not specified by {@code specifiedFormatter} parameter
  86      *                           nor found in LogManager configuration properties
  87      * @param specifiedFormatter if not null, this is the formatter to configure
  88      */
  89     Handler(Level defaultLevel, Formatter defaultFormatter,
  90             Formatter specifiedFormatter) {
  91 
  92         LogManager manager = LogManager.getLogManager();
  93         String cname = getClass().getName();
  94 
  95         final Level level = manager.getLevelProperty(cname + ".level", defaultLevel);
  96         final Filter filter = manager.getFilterProperty(cname + ".filter", null);
  97         final Formatter formatter = specifiedFormatter == null
  98                                     ? manager.getFormatterProperty(cname + ".formatter", defaultFormatter)
  99                                     : specifiedFormatter;
 100         final String encoding = manager.getStringProperty(cname + ".encoding", null);
 101 
 102         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 103             @Override
 104             public Void run() {
 105                 setLevel(level);
 106                 setFilter(filter);
 107                 setFormatter(formatter);
 108                 try {
 109                     setEncoding(encoding);
 110                 } catch (Exception ex) {
 111                     try {
 112                         setEncoding(null);
 113                     } catch (Exception ex2) {
 114                         // doing a setEncoding with null should always work.
 115                         // assert false;
 116                     }
 117                 }
 118                 return null;
 119             }
 120         }, null, LogManager.controlPermission);
 121     }
 122 
 123     /**
 124      * Publish a <tt>LogRecord</tt>.
 125      * <p>
 126      * The logging request was made initially to a <tt>Logger</tt> object,
 127      * which initialized the <tt>LogRecord</tt> and forwarded it here.
 128      * <p>
 129      * The <tt>Handler</tt>  is responsible for formatting the message, when and
 130      * if necessary.  The formatting should include localization.
 131      *
 132      * @param  record  description of the log event. A null record is
 133      *                 silently ignored and is not published
 134      */
 135     public abstract void publish(LogRecord record);
 136 
 137     /**
 138      * Flush any buffered output.
 139      */
 140     public abstract void flush();
 141 
 142     /**
 143      * Close the <tt>Handler</tt> and free all associated resources.
 144      * <p>
 145      * The close method will perform a <tt>flush</tt> and then close the
 146      * <tt>Handler</tt>.   After close has been called this <tt>Handler</tt>
 147      * should no longer be used.  Method calls may either be silently
 148      * ignored or may throw runtime exceptions.
 149      *
 150      * @exception  SecurityException  if a security manager exists and if
 151      *             the caller does not have <tt>LoggingPermission("control")</tt>.
 152      */
 153     public abstract void close() throws SecurityException;
 154 
 155     /**
 156      * Set a <tt>Formatter</tt>.  This <tt>Formatter</tt> will be used
 157      * to format <tt>LogRecords</tt> for this <tt>Handler</tt>.
 158      * <p>
 159      * Some <tt>Handlers</tt> may not use <tt>Formatters</tt>, in
 160      * which case the <tt>Formatter</tt> will be remembered, but not used.
 161      *
 162      * @param newFormatter the <tt>Formatter</tt> to use (may not be null)
 163      * @exception  SecurityException  if a security manager exists and if
 164      *             the caller does not have <tt>LoggingPermission("control")</tt>.
 165      */
 166     public synchronized void setFormatter(Formatter newFormatter) throws SecurityException {
 167         checkPermission();
 168         // Check for a null pointer:
 169         newFormatter.getClass();
 170         formatter = newFormatter;
 171     }
 172 
 173     /**
 174      * Return the <tt>Formatter</tt> for this <tt>Handler</tt>.
 175      * @return the <tt>Formatter</tt> (may be null).
 176      */
 177     public Formatter getFormatter() {
 178         return formatter;
 179     }
 180 
 181     /**
 182      * Set the character encoding used by this <tt>Handler</tt>.
 183      * <p>
 184      * The encoding should be set before any <tt>LogRecords</tt> are written
 185      * to the <tt>Handler</tt>.
 186      *
 187      * @param encoding  The name of a supported character encoding.
 188      *        May be null, to indicate the default platform encoding.
 189      * @exception  SecurityException  if a security manager exists and if
 190      *             the caller does not have <tt>LoggingPermission("control")</tt>.
 191      * @exception  UnsupportedEncodingException if the named encoding is
 192      *          not supported.
 193      */
 194     public synchronized void setEncoding(String encoding)
 195                         throws SecurityException, java.io.UnsupportedEncodingException {
 196         checkPermission();
 197         if (encoding != null) {
 198             try {
 199                 if(!java.nio.charset.Charset.isSupported(encoding)) {
 200                     throw new UnsupportedEncodingException(encoding);
 201                 }
 202             } catch (java.nio.charset.IllegalCharsetNameException e) {
 203                 throw new UnsupportedEncodingException(encoding);
 204             }
 205         }
 206         this.encoding = encoding;
 207     }
 208 
 209     /**
 210      * Return the character encoding for this <tt>Handler</tt>.
 211      *
 212      * @return  The encoding name.  May be null, which indicates the
 213      *          default encoding should be used.
 214      */
 215     public String getEncoding() {
 216         return encoding;
 217     }
 218 
 219     /**
 220      * Set a <tt>Filter</tt> to control output on this <tt>Handler</tt>.
 221      * <P>
 222      * For each call of <tt>publish</tt> the <tt>Handler</tt> will call
 223      * this <tt>Filter</tt> (if it is non-null) to check if the
 224      * <tt>LogRecord</tt> should be published or discarded.
 225      *
 226      * @param   newFilter  a <tt>Filter</tt> object (may be null)
 227      * @exception  SecurityException  if a security manager exists and if
 228      *             the caller does not have <tt>LoggingPermission("control")</tt>.
 229      */
 230     public synchronized void setFilter(Filter newFilter) throws SecurityException {
 231         checkPermission();
 232         filter = newFilter;
 233     }
 234 
 235     /**
 236      * Get the current <tt>Filter</tt> for this <tt>Handler</tt>.
 237      *
 238      * @return  a <tt>Filter</tt> object (may be null)
 239      */
 240     public Filter getFilter() {
 241         return filter;
 242     }
 243 
 244     /**
 245      * Define an ErrorManager for this Handler.
 246      * <p>
 247      * The ErrorManager's "error" method will be invoked if any
 248      * errors occur while using this Handler.
 249      *
 250      * @param em  the new ErrorManager
 251      * @exception  SecurityException  if a security manager exists and if
 252      *             the caller does not have <tt>LoggingPermission("control")</tt>.
 253      */
 254     public synchronized void setErrorManager(ErrorManager em) {
 255         checkPermission();
 256         if (em == null) {
 257            throw new NullPointerException();
 258         }
 259         errorManager = em;
 260     }
 261 
 262     /**
 263      * Retrieves the ErrorManager for this Handler.
 264      *
 265      * @return the ErrorManager for this Handler
 266      * @exception  SecurityException  if a security manager exists and if
 267      *             the caller does not have <tt>LoggingPermission("control")</tt>.
 268      */
 269     public ErrorManager getErrorManager() {
 270         checkPermission();
 271         return errorManager;
 272     }
 273 
 274    /**
 275      * Protected convenience method to report an error to this Handler's
 276      * ErrorManager.  Note that this method retrieves and uses the ErrorManager
 277      * without doing a security check.  It can therefore be used in
 278      * environments where the caller may be non-privileged.
 279      *
 280      * @param msg    a descriptive string (may be null)
 281      * @param ex     an exception (may be null)
 282      * @param code   an error code defined in ErrorManager
 283      */
 284     protected void reportError(String msg, Exception ex, int code) {
 285         try {
 286             errorManager.error(msg, ex, code);
 287         } catch (Exception ex2) {
 288             System.err.println("Handler.reportError caught:");
 289             ex2.printStackTrace();
 290         }
 291     }
 292 
 293     /**
 294      * Set the log level specifying which message levels will be
 295      * logged by this <tt>Handler</tt>.  Message levels lower than this
 296      * value will be discarded.
 297      * <p>
 298      * The intention is to allow developers to turn on voluminous
 299      * logging, but to limit the messages that are sent to certain
 300      * <tt>Handlers</tt>.
 301      *
 302      * @param newLevel   the new value for the log level
 303      * @exception  SecurityException  if a security manager exists and if
 304      *             the caller does not have <tt>LoggingPermission("control")</tt>.
 305      */
 306     public synchronized void setLevel(Level newLevel) throws SecurityException {
 307         if (newLevel == null) {
 308             throw new NullPointerException();
 309         }
 310         checkPermission();
 311         logLevel = newLevel;
 312     }
 313 
 314     /**
 315      * Get the log level specifying which messages will be
 316      * logged by this <tt>Handler</tt>.  Message levels lower
 317      * than this level will be discarded.
 318      * @return  the level of messages being logged.
 319      */
 320     public Level getLevel() {
 321         return logLevel;
 322     }
 323 
 324     /**
 325      * Check if this <tt>Handler</tt> would actually log a given <tt>LogRecord</tt>.
 326      * <p>
 327      * This method checks if the <tt>LogRecord</tt> has an appropriate
 328      * <tt>Level</tt> and  whether it satisfies any <tt>Filter</tt>.  It also
 329      * may make other <tt>Handler</tt> specific checks that might prevent a
 330      * handler from logging the <tt>LogRecord</tt>. It will return false if
 331      * the <tt>LogRecord</tt> is null.
 332      *
 333      * @param record  a <tt>LogRecord</tt>
 334      * @return true if the <tt>LogRecord</tt> would be logged.
 335      *
 336      */
 337     public boolean isLoggable(LogRecord record) {
 338         final int levelValue = getLevel().intValue();
 339         if (record.getLevel().intValue() < levelValue || levelValue == offValue) {
 340             return false;
 341         }
 342         final Filter filter = getFilter();
 343         if (filter == null) {
 344             return true;
 345         }
 346         return filter.isLoggable(record);
 347     }
 348 
 349     // Package-private support method for security checks.
 350     // We check that the caller has appropriate security privileges
 351     // to update Handler state and if not throw a SecurityException.
 352     void checkPermission() throws SecurityException {
 353         manager.checkPermission();
 354     }
 355 }