/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.util.logging;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
/**
* A {@code Handler} object accepts a logging request and exports the desired
* messages to a target, for example, a file, the console, etc. It can be
* disabled by setting its logging level to {@code Level.OFF}.
*/
public abstract class Handler {
private static final Level DEFAULT_LEVEL = Level.ALL;
// the error manager to report errors during logging
private ErrorManager errorMan;
// the character encoding used by this handler
private String encoding;
// the logging level
private Level level;
// the formatter used to export messages
private Formatter formatter;
// the filter used to filter undesired messages
private Filter filter;
// class name, used for property reading
private String prefix;
/**
* Constructs a {@code Handler} object with a default error manager instance
* {@code ErrorManager}, the default encoding, and the default logging
* level {@code Level.ALL}. It has no filter and no formatter.
*/
protected Handler() {
this.errorMan = new ErrorManager();
this.level = DEFAULT_LEVEL;
this.encoding = null;
this.filter = null;
this.formatter = null;
this.prefix = this.getClass().getName();
}
// get a instance from given class name, using Class.forName()
private Object getDefaultInstance(String className) {
Object result = null;
if (className == null) {
return result;
}
try {
result = Class.forName(className).newInstance();
} catch (Exception e) {
// ignore
}
return result;
}
// get a instance from given class name, using context classloader
private Object getCustomizeInstance(final String className) throws Exception {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = ClassLoader.getSystemClassLoader();
}
Class<?> c = loader.loadClass(className);
return c.newInstance();
}
// print error message in some format
void printInvalidPropMessage(String key, String value, Exception e) {
String msg = "Invalid property value for " + prefix + ":" + key + "/" + value;
errorMan.error(msg, e, ErrorManager.GENERIC_FAILURE);
}
/**
* init the common properties, including filter, level, formatter, and
* encoding
*/
void initProperties(String defaultLevel, String defaultFilter,
String defaultFormatter, String defaultEncoding) {
LogManager manager = LogManager.getLogManager();
// set filter
final String filterName = manager.getProperty(prefix + ".filter");
if (filterName != null) {
try {
filter = (Filter) getCustomizeInstance(filterName);
} catch (Exception e1) {
printInvalidPropMessage("filter", filterName, e1);
filter = (Filter) getDefaultInstance(defaultFilter);
}
} else {
filter = (Filter) getDefaultInstance(defaultFilter);
}
// set level
String levelName = manager.getProperty(prefix + ".level");
if (levelName != null) {
try {
level = Level.parse(levelName);
} catch (Exception e) {
printInvalidPropMessage("level", levelName, e);
level = Level.parse(defaultLevel);
}
} else {
level = Level.parse(defaultLevel);
}
// set formatter
final String formatterName = manager.getProperty(prefix + ".formatter");
if (formatterName != null) {
try {
formatter = (Formatter) getCustomizeInstance(formatterName);
} catch (Exception e) {
printInvalidPropMessage("formatter", formatterName, e);
formatter = (Formatter) getDefaultInstance(defaultFormatter);
}
} else {
formatter = (Formatter) getDefaultInstance(defaultFormatter);
}
// set encoding
final String encodingName = manager.getProperty(prefix + ".encoding");
try {
internalSetEncoding(encodingName);
} catch (UnsupportedEncodingException e) {
printInvalidPropMessage("encoding", encodingName, e);
}
}
/**
* Closes this handler. A flush operation will be performed and all the
* associated resources will be freed. Client applications should not use
* this handler after closing it.
*/
public abstract void close();
/**
* Flushes any buffered output.
*/
public abstract void flush();
/**
* Accepts a logging request and sends it to the the target.
*
* @param record
* the log record to be logged; {@code null} records are ignored.
*/
public abstract void publish(LogRecord record);
/**
* Gets the character encoding used by this handler, {@code null} for
* default encoding.
*
* @return the character encoding used by this handler.
*/
public String getEncoding() {
return this.encoding;
}
/**
* Gets the error manager used by this handler to report errors during
* logging.
*
* @return the error manager used by this handler.
*/
public ErrorManager getErrorManager() {
LogManager.getLogManager().checkAccess();
return this.errorMan;
}
/**
* Gets the filter used by this handler.
*
* @return the filter used by this handler (possibly {@code null}).
*/
public Filter getFilter() {
return this.filter;
}
/**
* Gets the formatter used by this handler to format the logging messages.
*
* @return the formatter used by this handler (possibly {@code null}).
*/
public Formatter getFormatter() {
return this.formatter;
}
/**
* Gets the logging level of this handler, records with levels lower than
* this value will be dropped.
*
* @return the logging level of this handler.
*/
public Level getLevel() {
return this.level;
}
/**
* Determines whether the supplied log record needs to be logged. The
* logging levels will be checked as well as the filter.
*
* @param record
* the log record to be checked.
* @return {@code true} if the supplied log record needs to be logged,
* otherwise {@code false}.
*/
public boolean isLoggable(LogRecord record) {
if (record == null) {
throw new NullPointerException("record == null");
}
if (this.level.intValue() == Level.OFF.intValue()) {
return false;
} else if (record.getLevel().intValue() >= this.level.intValue()) {
return this.filter == null || this.filter.isLoggable(record);
}
return false;
}
/**
* Reports an error to the error manager associated with this handler,
* {@code ErrorManager} is used for that purpose. No security checks are
* done, therefore this is compatible with environments where the caller
* is non-privileged.
*
* @param msg
* the error message, may be {@code null}.
* @param ex
* the associated exception, may be {@code null}.
* @param code
* an {@code ErrorManager} error code.
*/
protected void reportError(String msg, Exception ex, int code) {
this.errorMan.error(msg, ex, code);
}
/**
* Sets the character encoding used by this handler. A {@code null} value
* indicates the use of the default encoding. This internal method does
* not check security.
*
* @param newEncoding
* the character encoding to set.
* @throws UnsupportedEncodingException
* if the specified encoding is not supported by the runtime.
*/
void internalSetEncoding(String newEncoding) throws UnsupportedEncodingException {
// accepts "null" because it indicates using default encoding
if (newEncoding == null) {
this.encoding = null;
} else {
if (Charset.isSupported(newEncoding)) {
this.encoding = newEncoding;
} else {
throw new UnsupportedEncodingException(newEncoding);
}
}
}
/**
* Sets the character encoding used by this handler, {@code null} indicates
* a default encoding.
*
* @throws UnsupportedEncodingException if {@code charsetName} is not supported.
*/
public void setEncoding(String charsetName) throws UnsupportedEncodingException {
LogManager.getLogManager().checkAccess();
internalSetEncoding(charsetName);
}
/**
* Sets the error manager for this handler.
*
* @param newErrorManager
* the error manager to set.
* @throws NullPointerException
* if {@code em} is {@code null}.
*/
public void setErrorManager(ErrorManager newErrorManager) {
LogManager.getLogManager().checkAccess();
if (newErrorManager == null) {
throw new NullPointerException("newErrorManager == null");
}
this.errorMan = newErrorManager;
}
/**
* Sets the filter to be used by this handler.
*
* @param newFilter
* the filter to set, may be {@code null}.
*/
public void setFilter(Filter newFilter) {
LogManager.getLogManager().checkAccess();
this.filter = newFilter;
}
/**
* Sets the formatter to be used by this handler. This internal method does
* not check security.
*
* @param newFormatter
* the formatter to set.
*/
void internalSetFormatter(Formatter newFormatter) {
if (newFormatter == null) {
throw new NullPointerException("newFormatter == null");
}
this.formatter = newFormatter;
}
/**
* Sets the formatter to be used by this handler.
*
* @param newFormatter
* the formatter to set.
* @throws NullPointerException
* if {@code newFormatter} is {@code null}.
*/
public void setFormatter(Formatter newFormatter) {
LogManager.getLogManager().checkAccess();
internalSetFormatter(newFormatter);
}
/**
* Sets the logging level of the messages logged by this handler, levels
* lower than this value will be dropped.
*
* @param newLevel
* the logging level to set.
* @throws NullPointerException
* if {@code newLevel} is {@code null}.
*/
public void setLevel(Level newLevel) {
if (newLevel == null) {
throw new NullPointerException("newLevel == null");
}
LogManager.getLogManager().checkAccess();
this.level = newLevel;
}
}