/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.util; import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; import static org.opends.server.loggers.debug.DebugLogger.debugError; import org.opends.server.types.ErrorLogCategory; import org.opends.server.types.ErrorLogSeverity; import org.opends.server.messages.UtilityMessages; /** * This utility class provides static methods that make parameter checking * easier (e.g. in constructors and setters). * In particular the ensureNotNull methods provide an easy way to validate that * certain parameters are not null, and the ensureTrue methods provide the * ability to check arbitrary boolean conditions. *

* Invocation of these methods should be limited to situations where the only * way that they should fail is if there is a defect somewhere in the * system (including 3rd-party plugins). *

* You can think of these methods as being similar to assert, * but there are some additional advantages: *

* * In general, you should not worry about the performance impact of calling * these methods. Some micro-benchmarking has shown that ensureNotNull can be * called 200M times per second on a single CPU laptop. The value of catching * defects early will almost always out-weigh any overhead that is introduced. * There are a couple of exceptions to this. Any execution overhead that * happens before the method is invoked cannot be eliminated, e.g. * Validator.ensureTrue(someExpensiveCheck()) will always invoke * someExpensiveCheck(). When this code is on the critical path, * and we do not expect the validation to fail, you can guard the call with * an assert because each method returns true, and this code will * only be executed when asserts are enabled. *

* These methods are provided primarily to check parameter values for * constructors, setters, etc, and when they are used in this way, the javadoc * for the method must be updated to reflect what constraints are placed on the * parameters (e.g. attributeType cannot be null). *

* Feel free to add any method to this class that makes sense. Be sure to * ensure that they don't violate the spirit of this class in that performance * is second only to correctness. *

* There are a few issues open for remaining tasks: *

*/ public class Validator { /** This static final variable theoretically allows us to compile out all of * these checks. Since all of the code below is guarded with this check, * the compiler should eliminate it if ENABLE_CHECKS is false. * From doing a little bit of micro-benchmarking, it appears that setting * ENABLE_CHECKS=false speeds the code up by about a factor of four, but * it's still not the same as not having the invocation in the first place. * On a single CPU laptop, I was able to get 200M * invocations per second with ENABLE_CHECKS=true, and 350M with * ENABLE_CHECKS=false. *

* Setting this to false, will not eliminate any expensive computation * done in a parameter list (e.g. some self-check that returns true).*/ public static final boolean ENABLE_CHECKS = true; /** A one-based array for parameter descriptions. */ private static final String[] PARAM_DESCRIPTIONS = {"** A ZERO-BASED INDEX IS INVALID **", "(1st parameter)", "(2nd parameter)", "(3rd parameter)", "(4th parameter)", "(5th parameter)", "(6th parameter)", "(7th parameter)", "(8th parameter)", "(9th parameter)", "(10th parameter)"}; /** A count of the errors detected by the methods in this class since the * last time that resetErrorCount was called. */ private static long _errorCount = 0; /** * This method validates that the specified parameter is not null. It * throws an AssertionError if it is null after logging this error. *

* This should be used like an assert, except it is not turned * off at runtime. That is, it should only be used in situations where * there is a bug in someone's code if param is null. * * @param param the parameter to validate as non-null. * @return true always. This allows this call to be used in an assert * statement, which can skip this check and remove all * overhead from the calling code. This idiom should only be used when * performance testing proves that it is necessary. * * @throws AssertionError if and only if param is null * if assertions are enabled */ public static boolean ensureNotNull(Object param) throws AssertionError { if (ENABLE_CHECKS) { if (param == null) throwNull(""); } return true; } /** * This method validates that the specified parameters are not null. It * throws an AssertionError if one of them are null after logging this error. * It's similar to the ensureNotNull(Object) call except it provides the * convenience of checking two parameters at once. *

* This should be used like an assert, except it is not turned * off at runtime. That is, it should only be used in situations where * there is a bug in someone's code if param is null. *

* See the class level javadoc for why we did not use varargs to * implement this method. * * @param param1 the first parameter to validate as non-null. * @param param2 the second parameter to validate as non-null. * @return true always. This allows this call to be used in an assert * statement, which can skip this check and remove all * overhead from the calling code. This idiom should only be used when * performance testing proves that it is necessary. * * @throws AssertionError if and only if any of the parameters is null */ public static boolean ensureNotNull(Object param1, Object param2) throws AssertionError { if (ENABLE_CHECKS) { if (param1 == null) throwNull(PARAM_DESCRIPTIONS[1]); if (param2 == null) throwNull(PARAM_DESCRIPTIONS[2]); } return true; } /** * This method validates that the specified parameters are not null. It * throws an AssertionError if one of them are null after logging this error. * It's similar to the ensureNotNull(Object) call except it provides the * convenience of checking three parameters at once. *

* This should be used like an assert, except it is not turned * off at runtime. That is, it should only be used in situations where * there is a bug in someone's code if param is null. *

* See the class level javadoc for why we did not use varargs to * implement this method. * * @param param1 the first parameter to validate as non-null. * @param param2 the second parameter to validate as non-null. * @param param3 the third parameter to validate as non-null. * @return true always. This allows this call to be used in an assert * statement, which can skip this check and remove all * overhead from the calling code. This idiom should only be used when * performance testing proves that it is necessary. * * @throws AssertionError if and only if one of the parameters is null */ public static boolean ensureNotNull(Object param1, Object param2, Object param3) throws AssertionError { if (ENABLE_CHECKS) { if (param1 == null) throwNull(PARAM_DESCRIPTIONS[1]); if (param2 == null) throwNull(PARAM_DESCRIPTIONS[2]); if (param3 == null) throwNull(PARAM_DESCRIPTIONS[3]); } return true; } /** * This method validates that the specified parameters are not null. It * throws an AssertionError if one of them are null after logging this error. * It's similar to the ensureNotNull(Object) call except it provides the * convenience of checking four parameters at once. *

* This should be used like an assert, except it is not turned * off at runtime. That is, it should only be used in situations where * there is a bug in someone's code if param is null. *

* See the class level javadoc for why we did not use varargs to * implement this method. * * @param param1 the first parameter to validate as non-null. * @param param2 the second parameter to validate as non-null. * @param param3 the third parameter to validate as non-null. * @param param4 the fourth parameter to validate as non-null. * @return true always. This allows this call to be used in an assert * statement, which can skip this check and remove all * overhead from the calling code. This idiom should only be used when * performance testing proves that it is necessary. * * @throws AssertionError if and only if one of the parameters is null */ public static boolean ensureNotNull(Object param1, Object param2, Object param3, Object param4) throws AssertionError { if (ENABLE_CHECKS) { if (param1 == null) throwNull(PARAM_DESCRIPTIONS[1]); if (param2 == null) throwNull(PARAM_DESCRIPTIONS[2]); if (param3 == null) throwNull(PARAM_DESCRIPTIONS[3]); if (param4 == null) throwNull(PARAM_DESCRIPTIONS[4]); } return true; } /** * This method validates that the specified parameter is true. It * throws an AssertionError if it is not true. *

* This should be used like an assert, except it is not turned * off at runtime. That is, it should only be used in situations where * there is a bug in someone's code if param is null. The other advantage of * using this method instead of an assert is that it logs the error to the * debug and error logs. * * @param condition the condition that must be true. * @return true always. This allows this call to be used in an assert * statement, which can skip this check and remove all * overhead from the calling code. This idiom should only be used when * performance testing proves that it is necessary. * * @throws AssertionError if condition is false */ public static boolean ensureTrue(boolean condition) throws AssertionError { if (ENABLE_CHECKS) { if (!condition) { ensureTrue(condition, ""); } } return true; } /** * This method validates that the specified parameter is true. It * throws an AssertionError if it is not true. The supplied message is * included in the error message. *

* This should be used like an assert, except it is not turned * off at runtime. That is, it should only be used in situations where * there is a bug in someone's code if param is null. The other advantage of * using this method instead of an assert is that it logs the error to the * debug and error logs. * * @param condition the condition that must be true. * @param message the textual message to include in the error message. * @return true always. This allows this call to be used in an assert * statement, which can skip this check and remove all * overhead from the calling code. This idiom should only be used when * performance testing proves that it is necessary. * * @throws AssertionError if condition is false */ public static boolean ensureTrue(boolean condition, String message) throws AssertionError { if (ENABLE_CHECKS) { if (!condition) { String fullMessage = generateLineSpecificErrorMessage( "The specified condition must be true. " + message); logError(fullMessage); throw new AssertionError(fullMessage); } } return true; } //////////////////////////////////////////////////////////////////////////// // // ERROR COUNT // //////////////////////////////////////////////////////////////////////////// /** * Returns the number of errors that this class has detected since the * system started or the last time that resetErrorCount() was called. *

* This could be useful in the unit tests to validate that no background * errors occurred during a test. It could also be exposed by the monitoring * framekwork. * * @return the number of errors detected by this class since the error count * was last reset. */ public static synchronized long getErrorCount() { return _errorCount; } /** * Resets the error count to zero. */ public static synchronized void resetErrorCount() { _errorCount = 0; } private static synchronized void incrementErrorCount() { _errorCount++; } //////////////////////////////////////////////////////////////////////////// // // PRIVATE // //////////////////////////////////////////////////////////////////////////// private static String generateLineSpecificErrorMessage(String message) { return message + " The error occurred at " + getOriginalCallerLineInfo(); } private static void throwNull(String message) throws AssertionError { String fullMessage = generateLineSpecificErrorMessage( "The specified parameter must not be null. " + message); logError(fullMessage); throw new AssertionError(fullMessage); } private static void logError(String message) { incrementErrorCount(); String messageWithStack = message + ServerConstants.EOL + getCallingStack(); // Log to the debug log. if (debugEnabled()) { debugError(messageWithStack); } // Log to the error log. org.opends.server.loggers.Error.logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.SEVERE_ERROR, UtilityMessages.MSGID_VALIDATOR_PRECONDITION_NOT_MET, messageWithStack); } /* * @return a String representation of the line that called the * Validator method. */ private static String getOriginalCallerLineInfo() { StackTraceElement stackElements[] = Thread.currentThread().getStackTrace(); int callerIndex = getOriginalCallerStackIndex(stackElements); return stackElements[callerIndex].toString(); } /* * @return a stack trace rooted at the line that called the first * Validator method. */ private static String getCallingStack() { StackTraceElement stackElements[] = Thread.currentThread().getStackTrace(); int callerIndex = getOriginalCallerStackIndex(stackElements); StringBuilder buffer = new StringBuilder(); for (int i = callerIndex; i < stackElements.length; i++) { StackTraceElement stackElement = stackElements[i]; buffer.append(stackElement).append(ServerConstants.EOL); } return buffer.toString(); } /* * @return the index in the supplied stack trace of the first non-Validator * method. */ private static int getOriginalCallerStackIndex(StackTraceElement stack[]) { // Go up the stack until we find who called the first Validator method. StackTraceElement element = null; int i; for (i = 0; i < stack.length; i++) { element = stack[i]; // The stack trace of this thread looks like // java.lang.Thread.dumpThreads(Native Method) // java.lang.Thread.getStackTrace(Thread.java:1383) // org.opends.server.util.Validator.getOriginalCallerLineInfo... // ... // original caller <---- this is what we want to return // more stack if (!element.getClassName().equals(Validator.class.getName()) && !element.getClassName().equals(Thread.class.getName())) { break; } } return i; } }