package org.openntf.domino.logging;
import java.lang.reflect.Method;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openntf.formula.ASTNode;
import org.openntf.formula.FormulaParseException;
import org.openntf.formula.FormulaParser;
import org.openntf.formula.Formulas;
public class LogConfig {
public static final String cUserName = "$USER$";
public static final String cDBPath = "$DBPATH$";
public static final String cLoggerName = "$LOGGER$";
public static final String cLogMessage = "$MESSAGE$";
static class L_LogHandler {
String _handlerName;
String _handlerClassName;
Class<?> _handlerClass;
Method _handlerConfigFromProps;
Method _handlerGetInstance;
LogHandlerConfigIF _handlerConfig;
String _formatterClassName;
Class<?> _formatterClass;
Method _formatterGetInstance;
String _minimalLevelName;
Level _minimalLevel;
String _props;
boolean _inUse;
/*--------------------------------------------------------------*/
L_LogHandler(final String handlerName, final String handlerClassName, final String formatterClassName,
final String minimalLevelName, final String props) {
_handlerName = handlerName;
_handlerClassName = handlerClassName;
_formatterClassName = formatterClassName;
_minimalLevelName = minimalLevelName;
_props = props;
_inUse = false;
}
boolean checkYourself() {
try {
_handlerClass = Class.forName(_handlerClassName);
_handlerConfigFromProps = _handlerClass.getMethod("configFromProps", String.class);
_handlerGetInstance = _handlerClass.getMethod("getInstance", LogHandlerConfigIF.class, boolean.class);
if (_formatterClassName != null) {
_formatterClass = Class.forName(_formatterClassName);
_formatterGetInstance = _formatterClass.getMethod("getInstance");
}
Object o = _handlerConfigFromProps.invoke(null, _props);
if (!(o instanceof LogHandlerConfigIF))
throw new IllegalArgumentException(_handlerClassName + ".configFromProps returned " + o.getClass().getName());
_handlerConfig = (LogHandlerConfigIF) o;
} catch (Exception e) {
System.err.println("LogConfig: Caught " + e.getClass().getName() + ": " + e.getMessage());
e.printStackTrace();
return false;
}
if (_minimalLevelName != null)
if ((_minimalLevel = LogConfig.parseLevel(_minimalLevelName)) == null)
return false;
return true;
}
}
/*--------------------------------------------------------------*/
static class L_LogFilterHandler {
static class L_LogFilterConfigEntry {
static ThreadLocal<FormulaParser> _myFormulaParser = new ThreadLocal<FormulaParser>() {
@Override
protected FormulaParser initialValue() {
return Formulas.getMinimalParser();
}
};
String _logPrefix;
String _levelName;
Level _level;
String[] _logHandlerNames;
L_LogHandler[] _logHandlerObjs;
String _formulaCondition;
ASTNode _parsedCond;
boolean _condContUserName;
boolean _condContDBPath;
String _validUntilStr;
Date _validUntil;
/*--------------------------------------------------------------*/
public L_LogFilterConfigEntry(final String logPrefix, final String levelName, final String[] handlerNames,
final String formulaCondition, final String validUntilStr) {
_logPrefix = logPrefix;
_levelName = levelName;
_logHandlerNames = handlerNames;
_formulaCondition = formulaCondition;
_validUntilStr = validUntilStr;
}
boolean checkYourself(final String[] loggerNames) {
int i;
for (i = 0; i < loggerNames.length; i++)
if (_logPrefix.startsWith(loggerNames[i]))
break;
if (i == loggerNames.length) {
System.err.println("LogConfig: Invalid prefix: " + _logPrefix);
return false;
}
if ((_level = LogConfig.parseLevel(_levelName)) == null)
return false;
if (_formulaCondition != null) {
try {
_parsedCond = _myFormulaParser.get().parse(_formulaCondition);
} catch (FormulaParseException e) {
System.err.println("LogConfig: Invalid formula condition: " + _formulaCondition);
e.printStackTrace();
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
_condContUserName = _formulaCondition.contains(cUserName);
_condContDBPath = _formulaCondition.contains(cDBPath);
}
if (_validUntilStr != null) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
ParsePosition pos = new ParsePosition(0);
if ((_validUntil = sdf.parse(_validUntilStr, pos)) == null) {
System.err.println("LogConfig: Invalid validUntil: " + _validUntilStr);
return false;
}
_validUntil.setTime(_validUntil.getTime() + 24 * 3600 * 1000 - 1);
}
return true;
}
public boolean checkHandlers(final Map<String, L_LogHandler> definedHandlers) {
_logHandlerObjs = new L_LogHandler[_logHandlerNames.length];
for (int i = 0; i < _logHandlerNames.length; i++) {
if ((_logHandlerObjs[i] = definedHandlers.get(_logHandlerNames[i])) == null) {
System.err.println("LogConfig: Undefined Handler: " + _logHandlerNames[i]);
return false;
}
}
return true;
}
public void setHandlersInUse() {
for (int i = 0; i < _logHandlerObjs.length; i++)
_logHandlerObjs[i]._inUse = true;
}
}
/*--------------------------------------------------------------*/
String _myName;
String[] _loggerNames;
Logger[] _loggers; // A strong reference to the Logger itself is necessary in certain cases
String _defaultLevelName;
Level _defaultLevel;
String[] _defaultHandlerNames;
L_LogHandler[] _defaultHandlers;
List<L_LogFilterConfigEntry> _logFCEList;
LogFilterHandler _myHandler;
/*--------------------------------------------------------------*/
L_LogFilterHandler(final String yourName, final String[] loggerNames, final String defaultLevelName,
final String[] defaultHandlerNames) {
_myName = yourName;
_loggerNames = loggerNames;
_defaultLevelName = defaultLevelName;
_defaultHandlerNames = defaultHandlerNames;
_logFCEList = new ArrayList<L_LogFilterConfigEntry>();
}
boolean checkYourself() {
if ((_defaultLevel = LogConfig.parseLevel(_defaultLevelName)) == null)
return false;
for (L_LogFilterConfigEntry fce : _logFCEList)
if (!fce.checkYourself(_loggerNames))
return false;
return true;
}
public void addFCE(final String prefix, final String levelName, final String[] handlerNames, final String formulaCond,
final String validUntil) {
L_LogFilterHandler.L_LogFilterConfigEntry lfce = new L_LogFilterHandler.L_LogFilterConfigEntry(prefix, levelName, handlerNames,
formulaCond, validUntil);
_logFCEList.add(lfce);
}
public boolean checkHandlers(final Map<String, L_LogHandler> definedHandlers) {
_defaultHandlers = new L_LogHandler[_defaultHandlerNames.length];
for (int i = 0; i < _defaultHandlerNames.length; i++) {
if ((_defaultHandlers[i] = definedHandlers.get(_defaultHandlerNames[i])) == null) {
System.err.println("LogConfig: Undefined DefaultHandler: " + _defaultHandlerNames[i]);
return false;
}
}
for (L_LogFilterConfigEntry fce : _logFCEList)
if (!fce.checkHandlers(definedHandlers))
return false;
return true;
}
public void throwExpiredFCEs(final Date now) {
for (int i = _logFCEList.size() - 1; i >= 0; i--) {
L_LogFilterConfigEntry lfce = _logFCEList.get(i);
if (lfce._validUntil != null && lfce._validUntil.getTime() <= now.getTime()) {
System.out.println("HINT: Filter Config Entry expired due to ValidUntil=" + lfce._validUntilStr);
_logFCEList.remove(i);
}
}
}
public void setHandlersInUse() {
for (int i = 0; i < _defaultHandlers.length; i++)
_defaultHandlers[i]._inUse = true;
for (L_LogFilterConfigEntry fce : _logFCEList)
fce.setHandlersInUse();
}
}
/*--------------------------------------------------------------*/
Map<String, L_LogHandler> _logHandlers = new HashMap<String, L_LogHandler>();
Map<String, L_LogFilterHandler> _logFilterHandlers = new HashMap<String, L_LogFilterHandler>();
public static LogConfig fromProperties(final Properties props) {
LogConfig ret = new LogConfig();
if (!ret.initFromProperties(props))
return null;
return ret;
}
private boolean initFromProperties(final Properties props) {
String[] propList;
if ((propList = readAndCheckPropAsList(props, "Handlers", true)) == null)
return false;
for (int i = 0; i < propList.length; i++)
if (!initOneLogHandler(props, propList[i]))
return false;
if ((propList = readAndCheckPropAsList(props, "FilterHandlers", true)) == null)
return false;
for (int i = 0; i < propList.length; i++)
if (!initOneFilterHandler(props, propList[i]))
return false;
if (!checkLogHandlerRefs())
return false;
throwExpiredFCEs();
setHandlersInUse();
return true;
}
private boolean initOneLogHandler(final Properties props, final String handlerName) {
String keyPref = "Handler." + handlerName;
String className = readProp(props, keyPref + ".Class", true);
if (className == null)
return false;
String formatterClassName = readProp(props, keyPref + ".Formatter", false);
String minLevelName = readProp(props, keyPref + ".MinimalLevel", false);
String props4Handler = readProp(props, keyPref + ".Props", false);
L_LogHandler handler = new L_LogHandler(handlerName, className, formatterClassName, minLevelName, props4Handler);
_logHandlers.put(handlerName, handler);
return handler.checkYourself();
}
private boolean initOneFilterHandler(final Properties props, final String filterHandlerName) {
String keyPref = "FilterHandler." + filterHandlerName;
String[] loggerNames = readAndCheckPropAsList(props, keyPref + ".LoggerPrefices", true);
String defaultLevelName = readProp(props, keyPref + ".DefaultLevel", true);
String[] defaultHandlerNames = readAndCheckPropAsList(props, keyPref + ".DefaultHandlers", true);
if (loggerNames == null || defaultLevelName == null || defaultHandlerNames == null)
return false;
L_LogFilterHandler lfh = new L_LogFilterHandler(filterHandlerName, loggerNames, defaultLevelName, defaultHandlerNames);
_logFilterHandlers.put(filterHandlerName, lfh);
for (int i = 1;; i++) {
String keyPrefI = keyPref + ".FCE" + i;
String prefix = readProp(props, keyPrefI, false);
if (prefix == null)
break;
if (!initOneFCE(props, prefix, keyPrefI, lfh))
return false;
}
return lfh.checkYourself();
}
private boolean initOneFCE(final Properties props, final String prefix, final String keyPrefI, final L_LogFilterHandler lfh) {
String levelName = readProp(props, keyPrefI + ".Level", true);
String[] handlerNames = readAndCheckPropAsList(props, keyPrefI + ".Handlers", true);
if (levelName == null || handlerNames == null)
return false;
String formulaCond = readProp(props, keyPrefI + ".FormulaCondition", false);
String validUntil = readProp(props, keyPrefI + ".ValidUntil", false);
lfh.addFCE(prefix, levelName, handlerNames, formulaCond, validUntil);
return true;
}
private boolean checkLogHandlerRefs() {
for (L_LogFilterHandler lfh : _logFilterHandlers.values())
if (!lfh.checkHandlers(_logHandlers))
return false;
return true;
}
private void throwExpiredFCEs() {
Date now = new Date();
for (L_LogFilterHandler lfh : _logFilterHandlers.values())
lfh.throwExpiredFCEs(now);
}
private void setHandlersInUse() {
for (L_LogFilterHandler lfh : _logFilterHandlers.values())
lfh.setHandlersInUse();
}
/*--------------------------------------------------------------*/
private static String readProp(final Properties props, final String key, final boolean required) {
String ret = props.getProperty(key);
if (ret != null && ret.isEmpty())
ret = null;
if (required && ret == null)
System.err.println("LogConfig: Required Property " + key + " isn't supplied");
return ret;
}
private static String[] readPropAsList(final Properties props, final String key, final boolean required) {
String prop = readProp(props, key, required);
if (prop == null)
return null;
return splitAlongComma(prop);
}
public static String[] splitAlongComma(final String prop) {
String[] ret = prop.split(",");
for (int i = 0; i < ret.length; i++)
ret[i] = ret[i].trim();
Arrays.sort(ret);
return ret;
}
public static boolean checkPropList(final String key, final String[] propList) {
if (propList[0].isEmpty()) {
System.err.println("LogConfig: Multiple property for key " + key + " contains empty part");
return false;
}
for (int i = 0; i < propList.length - 1; i++)
if (propList[i].equals(propList[i + 1])) {
System.err.println("LogConfig: Multiple property for key " + key + " contains duplicates: " + propList[i]);
return false;
}
return true;
}
private static String[] readAndCheckPropAsList(final Properties props, final String key, final boolean required) {
String[] ret = readPropAsList(props, key, required);
if (ret != null)
if (!checkPropList(key, ret))
ret = null;
return ret;
}
/*--------------------------------------------------------------*/
public static Level parseLevel(final String levelName) {
if (Level.SEVERE.getName().equals(levelName))
return Level.SEVERE;
if (Level.WARNING.getName().equals(levelName))
return Level.WARNING;
if (Level.INFO.getName().equals(levelName))
return Level.INFO;
if (Level.CONFIG.getName().equals(levelName))
return Level.CONFIG;
if (Level.FINE.getName().equals(levelName))
return Level.FINE;
if (Level.FINER.getName().equals(levelName))
return Level.FINER;
if (Level.FINEST.getName().equals(levelName))
return Level.FINEST;
System.err.println("LogConfig.parseLevel: Invalid name for log level: " + levelName);
return null;
}
}