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