/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: Debug.java,v 1.6 2009/08/19 05:41:17 veiming Exp $ * * Portions Copyrighted 2013-2016 ForgeRock AS. * */ package com.sun.identity.shared.debug; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import org.slf4j.helpers.MessageFormatter; import com.sun.identity.shared.configuration.SystemPropertiesManager; import com.sun.identity.shared.debug.file.impl.StdDebugFile; import com.sun.identity.shared.debug.impl.DebugProviderImpl; // NOTE: Since JVM specs guarantee atomic access/updates to int variables // (actually all variables except double and long), the design consciously // avoids synchronized methods, particularly for message(). This is done to // reduce the performance overhead of synchronized message() when debugging // is disabled. This does not have serious side-effects other than an occasional // invocation of message() missing concurrent update of 'debugLevel'. /** * **************************************************************************** *

* Allows a uniform interface to file debug and exception information in a * uniform format. Debug supports different levels/states of * filing debug information (in the ascending order): OFF, * ERROR, WARNING, MESSAGE and * ON. A given debug level/state is enabled if the debug * state/level is set to at least that state/level. For example, if the debug * state is ERROR, only errors will be filed. If the debug state * is WARNING, only errors and warnings will be filed. If the * debug state is MESSAGE, everything will be filed. * MESSAGE and ON are of the same levels; the * difference between them being MESSAGE writes to a file, * whereas ON writes to System.out. *

*

* Debug service uses the property file, AMConfig.properties, to * set the default debug level and the output directory where the debug files * will be placed. The properties file is located (using * {@link java.util.ResourceBundle} semantics) from one of the directories in * the CLASSPATH. *

*

* The following keys are used to configure the Debug service. Possible values * for the key 'com.iplanet.services.debug.level' are: off | error | warning | * message. The key 'com.iplanet.services.debug.directory' specifies the output * directory where the debug files will be created. Optionally, the key * 'com.sun.identity.util.debug.provider' may be used to plugin a non-default * implementation of the debug service where necessary. *

*

*

*

*

 *  com.iplanet.services.debug.level
 *  com.iplanet.services.debug.directory
 *  com.sun.identity.util.debug.provider
 * 
*

*

*

* If there is an error reading or loading the properties, Debug service will * redirect all debug information to System.out *

* If these properties are changed, the server must be restarted for the changes * to take effect. *

*

* NOTE: Debugging is an IO intensive operation and may hurt application * performance when abused. Particularly, note that Java evaluates the arguments * to message() and warning() even when debugging * is turned off. It is recommended that the debug state be checked before * invoking any message() or warning() methods to * avoid unnecessary argument evaluation and to maximize application * performance. *

* * @supported.all.api */ public class Debug { /* Static fields and methods */ /** * flags the disabled debug state. */ public static final int OFF = 0; /** * flags the state where error debugging is enabled. When debugging is set * to less than ERROR, error debugging is also disabled. */ public static final int ERROR = 1; /** * flags the state where warning debugging is enabled, but message debugging * is disabled. When debugging is set to less than WARNING, * warning debugging is also disabled. */ public static final int WARNING = 2; /** * This state enables debugging of messages, warnings and errors. */ public static final int MESSAGE = 3; /** * flags the enabled debug state for warnings, errors and messages. Printing * to a file is disabled. All printing is done on System.out. */ public static final int ON = 4; /** * flags the disabled debug state. */ public static final String STR_OFF = "off"; /** * flags the state where error debugging is enabled. When debugging is set * to less than ERROR, error debugging is also disabled. */ public static final String STR_ERROR = "error"; /** * flags the state where warning debugging is enabled, but message debugging * is disabled. When debugging is set to less than WARNING, * warning debugging is also disabled. */ public static final String STR_WARNING = "warning"; /** * This state enables debugging of messages, warnings and errors. */ public static final String STR_MESSAGE = "message"; /** * flags the enables debug state for warnings, errors and messages. Printing * to a file is disabled. All printing is done on System.out. */ public static final String STR_ON = "on"; /** * debugMap is a container of all active Debug objects. Log file name is the * key and Debug is the value of this map. */ private static Map debugMap = new HashMap(); /** * serviceInitialized indicates if the service is already initialized. */ private static boolean serviceInitialized = false; /** * the provider instance that will be used for Debug service. */ private static IDebugProvider debugProvider; /** * Gets an existing instance of Debug for the specified debug name or a new * one if no such instance already exists. If a Debug object has to be * created, its level is set to the level defined in the * AMConfig.properties file. The level can be changed later * by using {@link #setDebug(int)} or {@link #setDebug(String)} methods. * * @param debugName name of the debug instances to be created * @return a Debug instance corresponding to the specified debug name. */ public static synchronized Debug getInstance(String debugName) { IDebug iDebug = getDebugProvider().getInstance(debugName); Debug debug = (Debug) getDebugMap().get(iDebug.getName()); if (debug == null) { debug = new Debug(iDebug); getDebugMap().put(iDebug.getName(), debug); } return debug; } /** * Returns a collection of all Debug instances that exist in the system at * the current instance. This is a live collection that will be updated as * and when new Debug instances are created. Note that if an iterator is * used, it could potentially cause a * ConcurrentModificationException if during the process of * iteration, the collection is modified by the system. * * @return a collection of all Debug instances in the system. */ public static Collection getInstances() { return getDebugMap().values(); } /** * Gets the Map of all Debug instances being used in the * system currently. * * @return the Map of all Debug instances */ private static Map getDebugMap() { return debugMap; } /** * Sets the provider instance to be used by Debug service. * * @param provider the IDebugProvider instance that is used by the * Debug service. */ private static void setDebugProvider(IDebugProvider provider) { debugProvider = provider; } /** * Gets the configured debug provider being used by the Debug service. * * @return the configured debug provider. */ static IDebugProvider getDebugProvider() { return debugProvider; } /** * Initializes the Debug Service by locating the SPI implementations and * instantiating the appropriate classes. */ private static synchronized void initialize() { if (!serviceInitialized) { String providerName = SystemPropertiesManager.get(DebugConstants.CONFIG_DEBUG_PROVIDER); IDebugProvider provider = null; boolean providerLoadFailed = false; Exception exceptionCatched = null; if (providerName != null && providerName.trim().length() > 0) { try { provider = (IDebugProvider) Class.forName(providerName).newInstance(); } catch (ClassNotFoundException cnex) { providerLoadFailed = true; exceptionCatched = cnex; } catch (InstantiationException iex) { providerLoadFailed = true; exceptionCatched = iex; } catch (IllegalAccessException iaex) { providerLoadFailed = true; exceptionCatched = iaex; } catch (ClassCastException ccex) { providerLoadFailed = true; exceptionCatched = ccex; } } if (provider == null) { if (providerLoadFailed) { ResourceBundle bundle = com.sun.identity.shared.locale.Locale.getInstallResourceBundle ("amUtilMsgs"); StdDebugFile.printError(Debug.class.getSimpleName(), bundle.getString("com.iplanet" + ".services" + ".debug.invalidprovider"), exceptionCatched); } provider = new DebugProviderImpl(); } setDebugProvider(provider); serviceInitialized = true; } } /* Instance fields and methods */ /** * The instance of the actual debug service class as obtained from the * configured provider. */ private IDebug debugServiceInstance; /** * Convinience method to query the name being used for this Debug instance. * The return value of this method is a string exactly equal to the name * that was used while creating this instance. * * @return the name of this Debug instance */ public String getName() { return getDebugServiceInstance().getName(); } /** * Checks if message debugging is enabled. *

*

* NOTE: Debugging is an IO intensive operation and may hurt * application performance when abused. Particularly, note that Java * evaluates arguments to message() even when debugging is * turned off. It is recommended that messageEnabled() be * called to check the debug state before invoking any * message() methods to avoid unnecessary argument evaluation * and maximize application performance. *

* * @return true if message debugging is enabled * false if message debugging is disabled */ public boolean messageEnabled() { return getDebugServiceInstance().messageEnabled(); } /** * Checks if warning debugging is enabled. *

*

* NOTE: Debugging is an IO intensive operation and may hurt * application performance when abused. Particularly, note that Java * evaluates arguments to warning() even when warning * debugging is turned off. It is recommended that * warningEnabled() be called to check the debug state before * invoking any warning() methods to avoid unnecessary * argument evaluation and maximize application performance. *

* * @return true if warning debugging is enabled * false if warning debugging is disabled */ public boolean warningEnabled() { return getDebugServiceInstance().warningEnabled(); } /** * Checks if error debugging is enabled. *

*

* NOTE: Debugging is an IO intensive operation and may hurt * application performance when abused. Particularly, note that Java * evaluates arguments to error() even when error debugging * is turned off. It is recommended that errorEnabled() be * called to check the debug state before invoking any error() * methods to avoid unnecessary argument evaluation and maximize application * performance. *

* * @return true if error debugging is enabled * false if error debugging is disabled */ public boolean errorEnabled() { return getDebugServiceInstance().errorEnabled(); } /** * Returns one of the five possible values: * * * @return the debug level */ public int getState() { return getDebugServiceInstance().getState(); } /** * Prints messages only when the debug state is either Debug.MESSAGE or * Debug.ON. *

*

* NOTE: Debugging is an IO intensive operation and may hurt * application performance when abused. Particularly, note that Java * evaluates arguments to message() even when debugging is * turned off. So when the argument to this method involves the String * concatenation operator '+' or any other method invocation, * messageEnabled MUST be used. It is recommended * that the debug state be checked by invoking messageEnabled() * before invoking any message() methods to avoid unnecessary * argument evaluation and maximize application performance. *

* * @param msg debug message. * @see Debug#message(String, Throwable) */ public void message(String msg) { getDebugServiceInstance().message(msg, null); } /** *

* Prints debug and exception messages only when the debug state is either * Debug.MESSAGE or Debug.ON. If the debug file is not accessible and * debugging is enabled, the message along with a time stamp and thread info * will be printed on System.out. *

*

*

* This method creates the debug file if does not exist; otherwise it starts * appending to the existing debug file. When invoked for the first time on * this object, the method writes a line delimiter of '*'s. *

*

*

* Note that the debug file will remain open until destroy() * is invoked. To conserve file resources, you should invoke * destroy() explicitly rather than wait for the garbage * collector to clean up. *

*

*

* NOTE: Debugging is an IO intensive operation and may hurt * application performance when abused. Particularly, note that Java * evaluates arguments to message() even when debugging is * turned off. It is recommended that the debug state be checked by invoking * messageEnabled() before invoking any * message() methods to avoid unnecessary argument evaluation * and to maximize application performance. *

* * @param msg message to be printed. A newline will be appended to the * message before printing either to System.out or * to the debug file. If msg is null, it is * ignored. * @param t Throwable, on which * printStackTrace will be invoked to print the * stack trace. If t is null, it is ignored. * @see Debug#error(String, Throwable) */ public void message(String msg, Throwable t) { getDebugServiceInstance().message(msg, t); } /** * A convenience method for message debug statements. The message will only be formatted * if the debug level is greater than {@link #WARNING}, and then it will be formatted using * the {@link org.slf4j.helpers.MessageFormatter} class. The relevant {@code message} method is then * called depending on whether the last parameter is an instance of {@code Throwable}. *

* This method is convenient way of issuing warning level debug statements without having * to guard the call with a check to {@link #messageEnabled()}, as that check is done * before evaluating the method parameters. *

* For this optimisation to work properly, this method should not be called using string * concatenation. If concatenation is required, it can be achieved using format patterns. *

* In this way, the only cost to execution is the assembly of the varargs parameter. * @param msg The debug message format, using {@link MessageFormatter} style format patterns. * @param params The parameters to the message, optionally with a {@code Throwable} as * the last parameter. */ public void message(String msg, Object... params) { IDebug debug = getDebugServiceInstance(); if (debug.messageEnabled()) { String message = MessageFormatter.arrayFormat(msg, params).getMessage(); if (params.length > 0 && params[params.length - 1] instanceof Throwable) { debug.message(message, (Throwable) params[params.length - 1]); } else { debug.message(message, null); } } } /** * Prints warning messages only when debug level is greater than * Debug.ERROR. *

*

* NOTE: Debugging is an IO intensive operation and may hurt * application performance when abused. Particularly, note that Java * evaluates arguments to warning() even when debugging is * turned off. So when the argument to this method involves the String * concatenation operator '+' or any other method invocation, * warningEnabled MUST be used. It is recommended * that the debug state be checked by invoking warningEnabled() * before invoking any warning() methods to avoid unnecessary * argument evaluation and to maximize application performance. *

* * @param msg message to be printed. A newline will be appended to the * message before printing either to System.out or * to the debug file. If msg is null, it is * ignored. * @see Debug#warning(String, Throwable) */ public void warning(String msg) { getDebugServiceInstance().warning(msg, null); } /** * Prints warning messages only when debug level is greater than * Debug.ERROR. *

*

* NOTE: Debugging is an IO intensive operation and may hurt * application performance when abused. Particularly, note that Java * evaluates arguments to warning() even when debugging is * turned off. It is recommended that the debug state be checked by invoking * warningEnabled() before invoking any * warning() methods to avoid unnecessary argument evaluation * and to maximize application performance. *

*

*

* If the debug file is not accessible and debugging is enabled, the message * along with a time stamp and thread info will be printed on * System.out. *

*

*

* This method creates the debug file if does not exist; otherwise it starts * appending to the existing debug file. When invoked for the first time on * this object, the method writes a line delimiter of '*'s. *

*

*

* Note that the debug file will remain open until destroy() * is invoked. To conserve file resources, you should invoke * destroy() explicitly rather than wait for the garbage * collector to clean up. *

* * @param msg message to be printed. A newline will be appended to the * message before printing either to System.out or * to the debug file. If msg is null, it is * ignored. * @param t Throwable, on which * printStackTrace() will be invoked to print the * stack trace. If t is null, it is ignored. */ public void warning(String msg, Throwable t) { getDebugServiceInstance().warning(msg, t); } /** * A convenience method for warning debug statements. The message will only be formatted * if the debug level is greater than {@link #ERROR}, and then it will be formatted using * the {@link org.slf4j.helpers.MessageFormatter} class. The relevant {@code warning} method is then * called depending on whether the last parameter is an instance of {@code Throwable}. *

* This method is convenient way of issuing warning level debug statements without having * to guard the call with a check to {@link #warningEnabled()}, as that check is done * before evaluating the method parameters. *

* For this optimisation to work properly, this method should not be called using string * concatenation. If concatenation is required, it can be achieved using format patterns. *

* In this way, the only cost to execution is the assembly of the varargs parameter. * @param msg The debug message format, using {@link MessageFormatter} style format patterns. * @param params The parameters to the message, optionally with a {@code Throwable} as * the last parameter. */ public void warning(String msg, Object... params) { IDebug debug = getDebugServiceInstance(); if (debug.warningEnabled()) { String message = MessageFormatter.arrayFormat(msg, params).getMessage(); if (params.length > 0 && params[params.length - 1] instanceof Throwable) { debug.warning(message, (Throwable) params[params.length - 1]); } else { debug.warning(message, null); } } } /** * Prints error messages only when debug level is greater than DEBUG.OFF. * * @param msg message to be printed. A newline will be appended to the * message before printing either to System.out or * to the debug file. If msg is null, it is * ignored. * @see Debug#error(String, Throwable) */ public void error(String msg) { getDebugServiceInstance().error(msg, null); } /** * Prints error messages only if debug state is greater than Debug.OFF. If * the debug file is not accessible and debugging is enabled, the message * along with a time stamp and thread info will be printed on * System.out. *

*

*

* This method creates the debug file if does not exist; otherwise it starts * appending to the existing debug file. When invoked for the first time on * this object, the method writes a line delimiter of '*'s. *

*

*

* Note that the debug file will remain open until destroy() * is invoked. To conserve file resources, you should invoke * destroy() explicitly rather than wait for the garbage * collector to clean up. *

* * @param msg message to be printed. A newline will be appended to the * message before printing either to System.out or * to the debug file. If msg is null, it is * ignored. * @param t Throwable, on which * printStackTrace() will be invoked to print the * stack trace. If t is null, it is ignored. */ public void error(String msg, Throwable t) { getDebugServiceInstance().error(msg, t); } /** * A convenience method for error debug statements. The message will only be formatted * if the debug level is greater than {@link #OFF}, and then it will be formatted using * the {@link org.slf4j.helpers.MessageFormatter} class. The relevant {@code error} method is then * called depending on whether the last parameter is an instance of {@code Throwable}. *

* This method is convenient way of issuing warning level debug statements without having * to guard the call with a check to {@link #errorEnabled()}, as that check is done * before evaluating the method parameters. *

* For this optimisation to work properly, this method should not be called using string * concatenation. If concatenation is required, it can be achieved using format patterns. *

* In this way, the only cost to execution is the assembly of the varargs parameter. * @param msg The debug message format, using {@link MessageFormatter} style format patterns. * @param params The parameters to the message, optionally with a {@code Throwable} as * the last parameter. */ public void error(String msg, Object... params) { IDebug debug = getDebugServiceInstance(); if (debug.errorEnabled()) { String message = MessageFormatter.arrayFormat(msg, params).getMessage(); if (params.length > 0 && params[params.length - 1] instanceof Throwable) { debug.error(message, (Throwable) params[params.length - 1]); } else { debug.error(message, null); } } } /** * Sets the debug capabilities based on the values of the * debugType argument. * * @param debugType is any one of five possible values: *

*/ public void setDebug(int debugType) { getDebugServiceInstance().setDebug(debugType); } /** * Allows runtime modification of the backend used by this instance. * by resetting the debug instance to reinitialize itself. * * @param mf merge flag - on for creating a single debug file. */ public void resetDebug(String mf) { getDebugServiceInstance().resetDebug(mf); } /** * Sets the debug capabilities based on the values of the * debugType argument. * * @param debugType is any one of the following possible values: * */ public void setDebug(String debugType) { getDebugServiceInstance().setDebug(debugType); } /** * Destroys the debug object, closes the debug file and releases any system * resources. Note that the debug file will remain open until * destroy() is invoked. To conserve file resources, you * should invoke destroy() explicitly rather than wait for * the garbage collector to clean up. *

*

* If this object is accessed after destroy() has been * invoked, the results are undefined. *

*/ public void destroy() { // No handling required } /** * Setter for setting the actual debug service class which is obtained from * the configured provider. */ private void setDebugServiceInstance(IDebug debugServiceInstance) { this.debugServiceInstance = debugServiceInstance; } /** * Returns the actual debug service class. * * @return The underlying debug service class. */ private IDebug getDebugServiceInstance() { return this.debugServiceInstance; } /** * The sole constructor of the Debug instances. This constructor is declared * private to ensure the use of the factory method provided in this class * called {@link #getInstance(String)}. */ private Debug(IDebug debugServiceInstance) { setDebugServiceInstance(debugServiceInstance); } /* Static Initializer */ static { initialize(); } }