Home | History | Annotate | Download | only in logging
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package java.util.logging;
     19 
     20 import java.io.UnsupportedEncodingException;
     21 import java.nio.charset.Charset;
     22 
     23 /**
     24  * A {@code Handler} object accepts a logging request and exports the desired
     25  * messages to a target, for example, a file, the console, etc. It can be
     26  * disabled by setting its logging level to {@code Level.OFF}.
     27  */
     28 public abstract class Handler {
     29 
     30     private static final Level DEFAULT_LEVEL = Level.ALL;
     31 
     32     // the error manager to report errors during logging
     33     private ErrorManager errorMan;
     34 
     35     // the character encoding used by this handler
     36     private String encoding;
     37 
     38     // the logging level
     39     private Level level;
     40 
     41     // the formatter used to export messages
     42     private Formatter formatter;
     43 
     44     // the filter used to filter undesired messages
     45     private Filter filter;
     46 
     47     // class name, used for property reading
     48     private String prefix;
     49 
     50     /**
     51      * Constructs a {@code Handler} object with a default error manager instance
     52      * {@code ErrorManager}, the default encoding, and the default logging
     53      * level {@code Level.ALL}. It has no filter and no formatter.
     54      */
     55     protected Handler() {
     56         this.errorMan = new ErrorManager();
     57         this.level = DEFAULT_LEVEL;
     58         this.encoding = null;
     59         this.filter = null;
     60         this.formatter = null;
     61         this.prefix = this.getClass().getName();
     62     }
     63 
     64     // get a instance from given class name, using Class.forName()
     65     private Object getDefaultInstance(String className) {
     66         Object result = null;
     67         if (className == null) {
     68             return result;
     69         }
     70         try {
     71             result = Class.forName(className).newInstance();
     72         } catch (Exception e) {
     73             // ignore
     74         }
     75         return result;
     76     }
     77 
     78     // get a instance from given class name, using context classloader
     79     private Object getCustomizeInstance(final String className) throws Exception {
     80         ClassLoader loader = Thread.currentThread().getContextClassLoader();
     81         if (loader == null) {
     82             loader = ClassLoader.getSystemClassLoader();
     83         }
     84         Class<?> c = loader.loadClass(className);
     85         return c.newInstance();
     86     }
     87 
     88     // print error message in some format
     89     void printInvalidPropMessage(String key, String value, Exception e) {
     90         String msg = "Invalid property value for " + prefix + ":" + key + "/" + value;
     91         errorMan.error(msg, e, ErrorManager.GENERIC_FAILURE);
     92     }
     93 
     94     /**
     95      * init the common properties, including filter, level, formatter, and
     96      * encoding
     97      */
     98     void initProperties(String defaultLevel, String defaultFilter,
     99             String defaultFormatter, String defaultEncoding) {
    100         LogManager manager = LogManager.getLogManager();
    101 
    102         // set filter
    103         final String filterName = manager.getProperty(prefix + ".filter");
    104         if (filterName != null) {
    105             try {
    106                 filter = (Filter) getCustomizeInstance(filterName);
    107             } catch (Exception e1) {
    108                 printInvalidPropMessage("filter", filterName, e1);
    109                 filter = (Filter) getDefaultInstance(defaultFilter);
    110             }
    111         } else {
    112             filter = (Filter) getDefaultInstance(defaultFilter);
    113         }
    114 
    115         // set level
    116         String levelName = manager.getProperty(prefix + ".level");
    117         if (levelName != null) {
    118             try {
    119                 level = Level.parse(levelName);
    120             } catch (Exception e) {
    121                 printInvalidPropMessage("level", levelName, e);
    122                 level = Level.parse(defaultLevel);
    123             }
    124         } else {
    125             level = Level.parse(defaultLevel);
    126         }
    127 
    128         // set formatter
    129         final String formatterName = manager.getProperty(prefix + ".formatter");
    130         if (formatterName != null) {
    131             try {
    132                 formatter = (Formatter) getCustomizeInstance(formatterName);
    133             } catch (Exception e) {
    134                 printInvalidPropMessage("formatter", formatterName, e);
    135                 formatter = (Formatter) getDefaultInstance(defaultFormatter);
    136             }
    137         } else {
    138             formatter = (Formatter) getDefaultInstance(defaultFormatter);
    139         }
    140 
    141         // set encoding
    142         final String encodingName = manager.getProperty(prefix + ".encoding");
    143         try {
    144             internalSetEncoding(encodingName);
    145         } catch (UnsupportedEncodingException e) {
    146             printInvalidPropMessage("encoding", encodingName, e);
    147         }
    148     }
    149 
    150     /**
    151      * Closes this handler. A flush operation will be performed and all the
    152      * associated resources will be freed. Client applications should not use
    153      * this handler after closing it.
    154      */
    155     public abstract void close();
    156 
    157     /**
    158      * Flushes any buffered output.
    159      */
    160     public abstract void flush();
    161 
    162     /**
    163      * Accepts a logging request and sends it to the the target.
    164      *
    165      * @param record
    166      *            the log record to be logged; {@code null} records are ignored.
    167      */
    168     public abstract void publish(LogRecord record);
    169 
    170     /**
    171      * Gets the character encoding used by this handler, {@code null} for
    172      * default encoding.
    173      *
    174      * @return the character encoding used by this handler.
    175      */
    176     public String getEncoding() {
    177         return this.encoding;
    178     }
    179 
    180     /**
    181      * Gets the error manager used by this handler to report errors during
    182      * logging.
    183      *
    184      * @return the error manager used by this handler.
    185      */
    186     public ErrorManager getErrorManager() {
    187         LogManager.getLogManager().checkAccess();
    188         return this.errorMan;
    189     }
    190 
    191     /**
    192      * Gets the filter used by this handler.
    193      *
    194      * @return the filter used by this handler (possibly {@code null}).
    195      */
    196     public Filter getFilter() {
    197         return this.filter;
    198     }
    199 
    200     /**
    201      * Gets the formatter used by this handler to format the logging messages.
    202      *
    203      * @return the formatter used by this handler (possibly {@code null}).
    204      */
    205     public Formatter getFormatter() {
    206         return this.formatter;
    207     }
    208 
    209     /**
    210      * Gets the logging level of this handler, records with levels lower than
    211      * this value will be dropped.
    212      *
    213      * @return the logging level of this handler.
    214      */
    215     public Level getLevel() {
    216         return this.level;
    217     }
    218 
    219     /**
    220      * Determines whether the supplied log record needs to be logged. The
    221      * logging levels will be checked as well as the filter.
    222      *
    223      * @param record
    224      *            the log record to be checked.
    225      * @return {@code true} if the supplied log record needs to be logged,
    226      *         otherwise {@code false}.
    227      */
    228     public boolean isLoggable(LogRecord record) {
    229         if (record == null) {
    230             throw new NullPointerException();
    231         }
    232         if (this.level.intValue() == Level.OFF.intValue()) {
    233             return false;
    234         } else if (record.getLevel().intValue() >= this.level.intValue()) {
    235             return this.filter == null || this.filter.isLoggable(record);
    236         }
    237         return false;
    238     }
    239 
    240     /**
    241      * Reports an error to the error manager associated with this handler,
    242      * {@code ErrorManager} is used for that purpose. No security checks are
    243      * done, therefore this is compatible with environments where the caller
    244      * is non-privileged.
    245      *
    246      * @param msg
    247      *            the error message, may be {@code null}.
    248      * @param ex
    249      *            the associated exception, may be {@code null}.
    250      * @param code
    251      *            an {@code ErrorManager} error code.
    252      */
    253     protected void reportError(String msg, Exception ex, int code) {
    254         this.errorMan.error(msg, ex, code);
    255     }
    256 
    257     /**
    258      * Sets the character encoding used by this handler. A {@code null} value
    259      * indicates the use of the default encoding. This internal method does
    260      * not check security.
    261      *
    262      * @param newEncoding
    263      *            the character encoding to set.
    264      * @throws UnsupportedEncodingException
    265      *             if the specified encoding is not supported by the runtime.
    266      */
    267     void internalSetEncoding(String newEncoding) throws UnsupportedEncodingException {
    268         // accepts "null" because it indicates using default encoding
    269         if (newEncoding == null) {
    270             this.encoding = null;
    271         } else {
    272             if (Charset.isSupported(newEncoding)) {
    273                 this.encoding = newEncoding;
    274             } else {
    275                 throw new UnsupportedEncodingException(newEncoding);
    276             }
    277         }
    278     }
    279 
    280     /**
    281      * Sets the character encoding used by this handler, {@code null} indicates
    282      * a default encoding.
    283      *
    284      * @param encoding
    285      *            the character encoding to set.
    286      * @throws UnsupportedEncodingException
    287      *             if the specified encoding is not supported by the runtime.
    288      */
    289     public void setEncoding(String encoding) throws UnsupportedEncodingException {
    290         LogManager.getLogManager().checkAccess();
    291         internalSetEncoding(encoding);
    292     }
    293 
    294     /**
    295      * Sets the error manager for this handler.
    296      *
    297      * @param em
    298      *            the error manager to set.
    299      * @throws NullPointerException
    300      *             if {@code em} is {@code null}.
    301      */
    302     public void setErrorManager(ErrorManager em) {
    303         LogManager.getLogManager().checkAccess();
    304         if (em == null) {
    305             throw new NullPointerException();
    306         }
    307         this.errorMan = em;
    308     }
    309 
    310     /**
    311      * Sets the filter to be used by this handler.
    312      *
    313      * @param newFilter
    314      *            the filter to set, may be {@code null}.
    315      */
    316     public void setFilter(Filter newFilter) {
    317         LogManager.getLogManager().checkAccess();
    318         this.filter = newFilter;
    319     }
    320 
    321     /**
    322      * Sets the formatter to be used by this handler. This internal method does
    323      * not check security.
    324      *
    325      * @param newFormatter
    326      *            the formatter to set.
    327      */
    328     void internalSetFormatter(Formatter newFormatter) {
    329         if (newFormatter == null) {
    330             throw new NullPointerException();
    331         }
    332         this.formatter = newFormatter;
    333     }
    334 
    335     /**
    336      * Sets the formatter to be used by this handler.
    337      *
    338      * @param newFormatter
    339      *            the formatter to set.
    340      * @throws NullPointerException
    341      *             if {@code newFormatter} is {@code null}.
    342      */
    343     public void setFormatter(Formatter newFormatter) {
    344         LogManager.getLogManager().checkAccess();
    345         internalSetFormatter(newFormatter);
    346     }
    347 
    348     /**
    349      * Sets the logging level of the messages logged by this handler, levels
    350      * lower than this value will be dropped.
    351      *
    352      * @param newLevel
    353      *            the logging level to set.
    354      * @throws NullPointerException
    355      *             if {@code newLevel} is {@code null}.
    356      */
    357     public void setLevel(Level newLevel) {
    358         if (newLevel == null) {
    359             throw new NullPointerException();
    360         }
    361         LogManager.getLogManager().checkAccess();
    362         this.level = newLevel;
    363     }
    364 }
    365