package com.hwlcn.ldap.util; import java.io.Serializable; import java.util.EnumSet; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import com.hwlcn.core.annotation.ThreadSafety; import com.hwlcn.ldap.asn1.ASN1Buffer; import com.hwlcn.ldap.asn1.ASN1Element; import com.hwlcn.ldap.ldap.protocol.LDAPResponse; import com.hwlcn.ldap.ldap.sdk.DisconnectType; import com.hwlcn.ldap.ldap.sdk.Entry; import com.hwlcn.ldap.ldap.sdk.LDAPConnection; import com.hwlcn.ldap.ldap.sdk.LDAPRequest; import com.hwlcn.ldap.ldif.LDIFRecord; import static com.hwlcn.ldap.util.StaticUtils.*; /** * This class provides a means of enabling and configuring debugging in the LDAP * SDK. * <BR><BR> * Access to debug information can be enabled through applications that use the * SDK by calling the {@link com.hwlcn.ldap.util.Debug#setEnabled} methods, or it can also be * enabled without any code changes through the use of system properties. In * particular, the {@link com.hwlcn.ldap.util.Debug#PROPERTY_DEBUG_ENABLED}, * {@link com.hwlcn.ldap.util.Debug#PROPERTY_DEBUG_LEVEL}, and {@link com.hwlcn.ldap.util.Debug#PROPERTY_DEBUG_TYPE} * properties may be used to control debugging without the need to alter any * code within the application that uses the SDK. * <BR><BR> * The LDAP SDK debugging subsystem uses the Java logging framework available * through the {@code java.util.logging} package with a logger name of * "{@code com.hwlcn.ldap.ldap.sdk}". The {@link com.hwlcn.ldap.util.Debug#getLogger} method may * be used to access the logger instance used by the LDAP SDK. * <BR><BR> * <H2>Example</H2> * The following example demonstrates the process that may be used to enable * debugging within the LDAP SDK and write information about all messages with * a {@code WARNING} level or higher to a file named "/tmp/test.log": * <PRE> * Debug.setEnabled(true); * Logger logger = Debug.getLogger(); * * FileHandler fileHandler = new FileHandler("/tmp/test.log"); * fileHandler.setLevel(Level.WARNING); * logger.addHandler(fileHandler); * </PRE> */ @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) public final class Debug implements Serializable { public static final String PROPERTY_DEBUG_ENABLED = "com.hwlcn.ldap.ldap.sdk.debug.enabled"; public static final String PROPERTY_INCLUDE_STACK_TRACE = "com.hwlcn.ldap.ldap.sdk.debug.includeStackTrace"; public static final String PROPERTY_DEBUG_LEVEL = "com.hwlcn.ldap.ldap.sdk.debug.level"; public static final String PROPERTY_DEBUG_TYPE = "com.hwlcn.ldap.ldap.sdk.debug.type"; public static final String LOGGER_NAME = "com.hwlcn.ldap.ldap.sdk"; private static final Logger logger = Logger.getLogger(LOGGER_NAME); private static final long serialVersionUID = -6079754380415146030L; private static boolean debugEnabled; private static boolean includeStackTrace; private static EnumSet<DebugType> debugTypes; static { initialize(System.getProperties()); } private Debug() { } public static void initialize() { includeStackTrace = false; debugEnabled = false; debugTypes = EnumSet.allOf(DebugType.class); logger.setLevel(Level.ALL); } public static void initialize(final Properties properties) { initialize(); if ((properties == null) || properties.isEmpty()) { return; } final String enabledProp = properties.getProperty(PROPERTY_DEBUG_ENABLED); if ((enabledProp != null) && (enabledProp.length() > 0)) { if (enabledProp.equalsIgnoreCase("true")) { debugEnabled = true; } else if (enabledProp.equalsIgnoreCase("false")) { debugEnabled = false; } else { throw new IllegalArgumentException("Invalid value '" + enabledProp + "' for property " + PROPERTY_DEBUG_ENABLED + ". The value must be either " + "'true' or 'false'."); } } final String stackProp = properties.getProperty(PROPERTY_INCLUDE_STACK_TRACE); if ((stackProp != null) && (stackProp.length() > 0)) { if (stackProp.equalsIgnoreCase("true")) { includeStackTrace = true; } else if (stackProp.equalsIgnoreCase("false")) { includeStackTrace = false; } else { throw new IllegalArgumentException("Invalid value '" + stackProp + "' for property " + PROPERTY_INCLUDE_STACK_TRACE + ". The value must be either " + "'true' or 'false'."); } } final String typesProp = properties.getProperty(PROPERTY_DEBUG_TYPE); if ((typesProp != null) && (typesProp.length() > 0)) { debugTypes = EnumSet.noneOf(DebugType.class); final StringTokenizer t = new StringTokenizer(typesProp, ", "); while (t.hasMoreTokens()) { final String debugTypeName = t.nextToken(); final DebugType debugType = DebugType.forName(debugTypeName); if (debugType == null) { throw new IllegalArgumentException("Invalid value '" + debugTypeName + "' for property " + PROPERTY_DEBUG_TYPE + ". Allowed values include: " + DebugType.getTypeNameList() + '.'); } else { debugTypes.add(debugType); } } } final String levelProp = properties.getProperty(PROPERTY_DEBUG_LEVEL); if ((levelProp != null) && (levelProp.length() > 0)) { logger.setLevel(Level.parse(levelProp)); } } public static Logger getLogger() { return logger; } public static boolean debugEnabled() { return debugEnabled; } public static boolean debugEnabled(final DebugType debugType) { return (debugEnabled && debugTypes.contains(debugType)); } public static void setEnabled(final boolean enabled) { debugTypes = EnumSet.allOf(DebugType.class); debugEnabled = enabled; } public static void setEnabled(final boolean enabled, final Set<DebugType> types) { if ((types == null) || types.isEmpty()) { debugTypes = EnumSet.allOf(DebugType.class); } else { debugTypes = EnumSet.copyOf(types); } debugEnabled = enabled; } public static boolean includeStackTrace() { return includeStackTrace; } public static void setIncludeStackTrace(final boolean includeStackTrace) { Debug.includeStackTrace = includeStackTrace; } public static EnumSet<DebugType> getDebugTypes() { return debugTypes; } public static void debugException(final Throwable t) { if (debugEnabled && debugTypes.contains(DebugType.EXCEPTION)) { debugException(Level.WARNING, t); } } public static void debugException(final Level l, final Throwable t) { if (debugEnabled && debugTypes.contains(DebugType.EXCEPTION)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); buffer.append("caughtException=\""); getStackTrace(t, buffer); buffer.append('"'); logger.log(l, buffer.toString(), t); } } public static void debugConnect(final String h, final int p) { if (debugEnabled && debugTypes.contains(DebugType.CONNECT)) { debugConnect(Level.INFO, h, p, null); } } public static void debugConnect(final Level l, final String h, final int p) { if (debugEnabled && debugTypes.contains(DebugType.CONNECT)) { debugConnect(l, h, p, null); } } public static void debugConnect(final String h, final int p, final LDAPConnection c) { if (debugEnabled && debugTypes.contains(DebugType.CONNECT)) { debugConnect(Level.INFO, h, p, c); } } public static void debugConnect(final Level l, final String h, final int p, final LDAPConnection c) { if (debugEnabled && debugTypes.contains(DebugType.CONNECT)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); buffer.append("connectedTo=\""); buffer.append(h); buffer.append(':'); buffer.append(p); buffer.append('"'); if (c != null) { buffer.append(" connectionID="); buffer.append(c.getConnectionID()); final String connectionName = c.getConnectionName(); if (connectionName != null) { buffer.append(" connectionName=\""); buffer.append(connectionName); buffer.append('"'); } final String connectionPoolName = c.getConnectionPoolName(); if (connectionPoolName != null) { buffer.append(" connectionPoolName=\""); buffer.append(connectionPoolName); buffer.append('"'); } } logger.log(l, buffer.toString()); } } public static void debugDisconnect(final String h, final int p, final DisconnectType t, final String m, final Throwable e) { if (debugEnabled && debugTypes.contains(DebugType.CONNECT)) { debugDisconnect(Level.INFO, h, p, null, t, m, e); } } public static void debugDisconnect(final Level l, final String h, final int p, final DisconnectType t, final String m, final Throwable e) { if (debugEnabled && debugTypes.contains(DebugType.CONNECT)) { debugDisconnect(l, h, p, null, t, m, e); } } public static void debugDisconnect(final String h, final int p, final LDAPConnection c, final DisconnectType t, final String m, final Throwable e) { if (debugEnabled && debugTypes.contains(DebugType.CONNECT)) { debugDisconnect(Level.INFO, h, p, c, t, m, e); } } public static void debugDisconnect(final Level l, final String h, final int p, final LDAPConnection c, final DisconnectType t, final String m, final Throwable e) { if (debugEnabled && debugTypes.contains(DebugType.CONNECT)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); if (c != null) { buffer.append("connectionID="); buffer.append(c.getConnectionID()); final String connectionName = c.getConnectionName(); if (connectionName != null) { buffer.append(" connectionName=\""); buffer.append(connectionName); buffer.append('"'); } final String connectionPoolName = c.getConnectionPoolName(); if (connectionPoolName != null) { buffer.append(" connectionPoolName=\""); buffer.append(connectionPoolName); buffer.append('"'); } buffer.append(' '); } buffer.append("disconnectedFrom=\""); buffer.append(h); buffer.append(':'); buffer.append(p); buffer.append("\" disconnectType=\""); buffer.append(t.name()); buffer.append('"'); if (m != null) { buffer.append("\" disconnectMessage=\""); buffer.append(m); buffer.append('"'); } if (e != null) { buffer.append("\" disconnectCause=\""); getStackTrace(e, buffer); buffer.append('"'); } logger.log(l, buffer.toString(), c); } } public static void debugLDAPRequest(final LDAPRequest r) { if (debugEnabled && debugTypes.contains(DebugType.LDAP)) { debugLDAPRequest(Level.INFO, r, -1, null); } } public static void debugLDAPRequest(final Level l, final LDAPRequest r) { if (debugEnabled && debugTypes.contains(DebugType.LDAP)) { debugLDAPRequest(l, r, -1, null); } } public static void debugLDAPRequest(final LDAPRequest r, final int i, final LDAPConnection c) { if (debugEnabled && debugTypes.contains(DebugType.LDAP)) { debugLDAPRequest(Level.INFO, r, i, c); } } public static void debugLDAPRequest(final Level l, final LDAPRequest r, final int i, final LDAPConnection c) { if (debugEnabled && debugTypes.contains(DebugType.LDAP)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); if (c != null) { buffer.append("connectionID="); buffer.append(c.getConnectionID()); final String connectionName = c.getConnectionName(); if (connectionName != null) { buffer.append(" connectionName=\""); buffer.append(connectionName); buffer.append('"'); } final String connectionPoolName = c.getConnectionPoolName(); if (connectionPoolName != null) { buffer.append(" connectionPoolName=\""); buffer.append(connectionPoolName); buffer.append('"'); } buffer.append(" connectedTo=\""); buffer.append(c.getConnectedAddress()); buffer.append(':'); buffer.append(c.getConnectedPort()); buffer.append("\" "); } if (i >= 0) { buffer.append(" messageID="); buffer.append(i); buffer.append(' '); } buffer.append("sendingLDAPRequest=\""); r.toString(buffer); buffer.append('"'); logger.log(l, buffer.toString()); } } public static void debugLDAPResult(final LDAPResponse r) { if (debugEnabled && debugTypes.contains(DebugType.LDAP)) { debugLDAPResult(Level.INFO, r, null); } } public static void debugLDAPResult(final Level l, final LDAPResponse r) { if (debugEnabled && debugTypes.contains(DebugType.LDAP)) { debugLDAPResult(l, r, null); } } public static void debugLDAPResult(final LDAPResponse r, final LDAPConnection c) { if (debugEnabled && debugTypes.contains(DebugType.LDAP)) { debugLDAPResult(Level.INFO, r, c); } } public static void debugLDAPResult(final Level l, final LDAPResponse r, final LDAPConnection c) { if (debugEnabled && debugTypes.contains(DebugType.LDAP)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); if (c != null) { buffer.append("connectionID="); buffer.append(c.getConnectionID()); final String connectionName = c.getConnectionName(); if (connectionName != null) { buffer.append(" connectionName=\""); buffer.append(connectionName); buffer.append('"'); } final String connectionPoolName = c.getConnectionPoolName(); if (connectionPoolName != null) { buffer.append(" connectionPoolName=\""); buffer.append(connectionPoolName); buffer.append('"'); } buffer.append(" connectedTo=\""); buffer.append(c.getConnectedAddress()); buffer.append(':'); buffer.append(c.getConnectedPort()); buffer.append("\" "); } buffer.append("readLDAPResult=\""); r.toString(buffer); buffer.append('"'); logger.log(l, buffer.toString()); } } public static void debugASN1Write(final ASN1Element e) { if (debugEnabled && debugTypes.contains(DebugType.ASN1)) { debugASN1Write(Level.INFO, e); } } public static void debugASN1Write(final Level l, final ASN1Element e) { if (debugEnabled && debugTypes.contains(DebugType.ASN1)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); buffer.append("writingASN1Element=\""); e.toString(buffer); buffer.append('"'); logger.log(l, buffer.toString()); } } public static void debugASN1Write(final ASN1Buffer b) { if (debugEnabled && debugTypes.contains(DebugType.ASN1)) { debugASN1Write(Level.INFO, b); } } public static void debugASN1Write(final Level l, final ASN1Buffer b) { if (debugEnabled && debugTypes.contains(DebugType.ASN1)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); buffer.append("writingASN1Element=\""); toHex(b.toByteArray(), buffer); buffer.append('"'); logger.log(l, buffer.toString()); } } public static void debugASN1Read(final ASN1Element e) { if (debugEnabled && debugTypes.contains(DebugType.ASN1)) { debugASN1Read(Level.INFO, e); } } public static void debugASN1Read(final Level l, final ASN1Element e) { if (debugEnabled && debugTypes.contains(DebugType.ASN1)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); buffer.append("readASN1Element=\""); e.toString(buffer); buffer.append('"'); logger.log(l, buffer.toString()); } } public static void debugLDIFWrite(final LDIFRecord r) { if (debugEnabled && debugTypes.contains(DebugType.LDIF)) { debugLDIFWrite(Level.INFO, r); } } public static void debugLDIFWrite(final Level l, final LDIFRecord r) { if (debugEnabled && debugTypes.contains(DebugType.LDIF)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); buffer.append("writingLDIFRecord=\""); r.toString(buffer); buffer.append('"'); logger.log(l, buffer.toString()); } } public static void debugLDIFRead(final LDIFRecord r) { if (debugEnabled && debugTypes.contains(DebugType.LDIF)) { debugLDIFRead(Level.INFO, r); } } public static void debugLDIFRead(final Level l, final LDIFRecord r) { if (debugEnabled && debugTypes.contains(DebugType.LDIF)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); buffer.append("readLDIFRecord=\""); r.toString(buffer); buffer.append('"'); logger.log(l, buffer.toString()); } } public static void debugMonitor(final Entry e, final String m) { if (debugEnabled && debugTypes.contains(DebugType.MONITOR)) { debugMonitor(Level.FINE, e, m); } } public static void debugMonitor(final Level l, final Entry e, final String m) { if (debugEnabled && debugTypes.contains(DebugType.MONITOR)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); buffer.append("monitorEntryDN=\""); buffer.append(e.getDN()); buffer.append("\" message=\""); buffer.append(m); buffer.append('"'); logger.log(l, buffer.toString()); } } public static void debugCodingError(final Throwable t) { if (debugEnabled && debugTypes.contains(DebugType.CODING_ERROR)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, Level.SEVERE); buffer.append("codingError=\""); getStackTrace(t, buffer); buffer.append('"'); logger.log(Level.SEVERE, buffer.toString()); } } public static void debug(final Level l, final DebugType t, final String m) { if (debugEnabled && debugTypes.contains(t)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); buffer.append("message=\""); buffer.append(m); buffer.append('"'); logger.log(l, buffer.toString()); } } public static void debug(final Level l, final DebugType t, final String m, final Throwable e) { if (debugEnabled && debugTypes.contains(t)) { final StringBuilder buffer = new StringBuilder(); addCommonHeader(buffer, l); buffer.append("message=\""); buffer.append(m); buffer.append('"'); buffer.append(" exception=\""); getStackTrace(e, buffer); buffer.append('"'); logger.log(l, buffer.toString(), e); } } private static void addCommonHeader(final StringBuilder buffer, final Level level) { buffer.append("level=\""); buffer.append(level.getName()); buffer.append("\" threadID="); buffer.append(Thread.currentThread().getId()); buffer.append(" threadName=\""); buffer.append(Thread.currentThread().getName()); if (includeStackTrace) { buffer.append("\" calledFrom=\""); boolean appended = false; boolean foundDebug = false; for (final StackTraceElement e : Thread.currentThread().getStackTrace()) { final String className = e.getClassName(); if (className.equals(Debug.class.getName())) { foundDebug = true; } else if (foundDebug) { if (appended) { buffer.append(" / "); } appended = true; buffer.append(e.getMethodName()); buffer.append('('); buffer.append(e.getFileName()); final int lineNumber = e.getLineNumber(); if (lineNumber > 0) { buffer.append(':'); buffer.append(lineNumber); } else if (e.isNativeMethod()) { buffer.append(":native"); } buffer.append(')'); } } } buffer.append("\" revision="); buffer.append(' '); } }