/*
|
* 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
|
*
|
*
|
* Copyright 2006-2008 Sun Microsystems, Inc.
|
*/
|
package org.opends.server.util;
|
import org.opends.messages.Message;
|
|
import static org.opends.server.loggers.debug.DebugLogger.*;
|
import org.opends.server.loggers.debug.DebugTracer;
|
import org.opends.server.loggers.ErrorLogger;
|
|
/**
|
* 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.
|
* <p>
|
* 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).
|
* <p>
|
* You can think of these methods as being similar to <code>assert</code>,
|
* but there are some additional advantages:
|
* <ul>
|
* <li>All failures are logged to the debug and error logs</li>
|
* <li>Checks are always enabled (even if asserts are off)</li>
|
* <li>This class tracks the number of failures, allowing it to
|
* be exposed via monitoring, etc.<li>
|
* <li>The unit tests can track unnoticed internal failures and
|
* report on them.</li>
|
* <li>Developers can catch all Validator failures with a single
|
* break point.</li>
|
* </ul>
|
*
|
* 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.
|
* <code>Validator.ensureTrue(someExpensiveCheck())</code> will always invoke
|
* <code>someExpensiveCheck()</code>. When this code is on the critical path,
|
* and we do not expect the validation to fail, you can guard the call with
|
* an <code>assert</code> because each method returns true, and this code will
|
* only be executed when asserts are enabled.
|
* <p>
|
* 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).
|
* <p>
|
* 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.
|
* <p>
|
* There are a few issues open for remaining tasks:
|
* <ul>
|
* <li>757 Validator should expose a way to turn it off</li>
|
* <li>758 Validator should provide a way to throttle it's error messages</li>
|
* <li>759 Unit tests should always check that no unexpected Validator checks
|
* failed</li>
|
* </ul>
|
*/
|
@org.opends.server.types.PublicAPI(
|
stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
|
mayInstantiate=false,
|
mayExtend=false,
|
mayInvoke=true)
|
public class Validator {
|
/**
|
* The tracer object for the debug logger.
|
*/
|
private static final DebugTracer TRACER = getTracer();
|
|
/** 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.
|
* <p>
|
* 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.
|
* <p>
|
* 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.
|
* <p>
|
* 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.
|
* <p>
|
* 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.
|
* <p>
|
* 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.
|
* <p>
|
* 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.
|
* <p>
|
* 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.
|
* <p>
|
* 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.
|
* <p>
|
* 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.
|
* <p>
|
* 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) {
|
StringBuilder mb = new StringBuilder();
|
mb.append("The specified condition must be true. ");
|
mb.append(message);
|
String fullString = generateLineSpecificErrorString(mb.toString());
|
|
logError(fullString);
|
|
throw new AssertionError(fullString);
|
}
|
}
|
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.
|
* <p>
|
* 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 generateLineSpecificErrorString(String message) {
|
StringBuilder mb = new StringBuilder();
|
mb.append(message);
|
mb.append(" The error occurred at ");
|
mb.append(getOriginalCallerLineInfo());
|
return mb.toString();
|
}
|
|
|
private static void throwNull(String message)
|
throws AssertionError {
|
StringBuilder mb = new StringBuilder();
|
mb.append("The specified parameter must not be null. ");
|
mb.append(message);
|
String fullString = generateLineSpecificErrorString(mb.toString());
|
|
logError(fullString);
|
|
throw new AssertionError(fullString);
|
}
|
|
|
|
private static void logError(String message) {
|
incrementErrorCount();
|
|
StringBuilder mb = new StringBuilder();
|
mb.append(message);
|
mb.append(ServerConstants.EOL);
|
mb.append(getCallingStack());
|
String messageWithStack = mb.toString();
|
|
// Log to the debug log.
|
if (debugEnabled())
|
{
|
TRACER.debugError(messageWithStack.toString());
|
}
|
|
// Log to the error log.
|
ErrorLogger.logError(Message.raw(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;
|
}
|
}
|