mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

davidely
10.27.2006 9258a861ce9acb7a6f380c0db8fce481cacfc5f0
Added ensureNotNull and ensureTrue functionality via the new Validator class. Thanks to Neil and Andy for the reviews.
2 files added
1 files modified
659 ■■■■■ changed files
opendj-sdk/opends/src/server/org/opends/server/messages/UtilityMessages.java 13 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/util/Validator.java 479 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/util/ValidatorTests.java 167 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/messages/UtilityMessages.java
@@ -1583,6 +1583,14 @@
       CATEGORY_MASK_UTIL | SEVERITY_MASK_MILD_ERROR | 147;
  /**
   * The message ID for the message that will be used if a Validator check
   * fails.  This signals that there is a method invocation that violates
   * the pre-conditions of the method.
   */
  public static final int MSGID_VALIDATOR_PRECONDITION_NOT_MET =
       CATEGORY_MASK_UTIL | SEVERITY_MASK_SEVERE_ERROR | 148;
  /**
   * Associates a set of generic messages with the message IDs defined in this
@@ -2108,6 +2116,11 @@
                    "file mode.  UNIX file modes must be a three-character " +
                    "string in which each character is a numeric digit " +
                    "between zero and seven.");
    registerMessage(MSGID_VALIDATOR_PRECONDITION_NOT_MET,
                    "A precondition of the invoked method was not met.  This " +
                    "This usually means there is a defect somewhere in the " +
                    "call stack.  Details: %s");
  }
}
opendj-sdk/opends/src/server/org/opends/server/util/Validator.java
New file
@@ -0,0 +1,479 @@
/*
 * 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 Sun Microsystems, Inc.
 */
package org.opends.server.util;
import static org.opends.server.loggers.Debug.debugMessage;
import org.opends.server.types.DebugLogCategory;
import org.opends.server.types.DebugLogSeverity;
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.
 * <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 failure, 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>
 */
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.
   *  <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;
  /** The fully-qualified class name for this class, which is used for
   *  debugging purposes. */
  private static final String CLASS_NAME = Validator.class.getName();
  /** 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 a RuntimeException or 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 NullPointerException if and only if param is null
   * @throws AssertionError this is thrown instead of NullPointerException
   * if assertions are enabled
   */
  public static boolean ensureNotNull(Object param)
          throws NullPointerException, AssertionError {
    if (ENABLE_CHECKS) {
      if (param == null) throwNull("");
    }
    return true;
  }
  /**
   * This method validates that the specified parameters are not null.  It
   * throws a RuntimeException or 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 NullPointerException if and only if one of the parameters is null
   * @throws AssertionError this is thrown instead of NullPointerException
   * if assertions are enabled
   */
  public static boolean ensureNotNull(Object param1, Object param2)
          throws NullPointerException, 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 a RuntimeException or 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 NullPointerException if and only if one of the parameters is null
   * @throws AssertionError this is thrown instead of NullPointerException
   * if assertions are enabled
   */
  public static boolean ensureNotNull(Object param1, Object param2,
                                      Object param3)
          throws NullPointerException, 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 a RuntimeException or 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.
   * @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 NullPointerException if and only if one of the parameters is null
   * @throws AssertionError this is thrown instead of NullPointerException
   * if assertions are enabled
   */
  public static boolean ensureNotNull(Object param1, Object param2,
                                      Object param3, Object param4)
          throws NullPointerException, 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 a RuntimeException or 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 and assertions are enabled
   * @throws IllegalArgumentException if condition is false and assertions are
   * not enabled
   */
  public static boolean ensureTrue(boolean condition)
          throws IllegalArgumentException, AssertionError {
    if (ENABLE_CHECKS) {
      if (!condition) {
        ensureTrue(condition, "");
      }
    }
    return true;
  }
  /**
   * This method validates that the specified parameter is true.  It
   * throws a RuntimeException or 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 and assertions are enabled
   * @throws IllegalArgumentException if condition is false and assertions are
   * not enabled
   */
  public static boolean ensureTrue(boolean condition, String message)
          throws IllegalArgumentException, AssertionError {
    if (ENABLE_CHECKS) {
      if (!condition) {
        String fullMessage = generateLineSpecificErrorMessage(
                "The specified condition must be true. " + message);
        logError(fullMessage);
        // Prefer throwing an assertion if they are enabled.
        assert condition : fullMessage;
        throw new IllegalArgumentException(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.
   * <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 generateLineSpecificErrorMessage(String message) {
    return message + "  The error occurred at " + getOriginalCallerLineInfo();
  }
  private static void throwNull(String message)
          throws NullPointerException, AssertionError {
    String fullMessage = generateLineSpecificErrorMessage(
            "The specified parameter must not be null. " + message);
    logError(fullMessage);
    // Prefer throwing an assertion if they are enabled.
    assert false : fullMessage;
    throw new NullPointerException(fullMessage);
  }
  private static void logError(String message) {
    incrementErrorCount();
    String messageWithStack = message + ServerConstants.EOL + getCallingStack();
    // Log to the debug log.
    debugMessage(DebugLogCategory.CORE_SERVER, DebugLogSeverity.ERROR,
            CLASS_NAME, "logError", 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;
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/util/ValidatorTests.java
New file
@@ -0,0 +1,167 @@
/*
 * 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 Sun Microsystems, Inc.
 */
package org.opends.server.util;
import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
import static org.testng.Assert.*;
/**
 * Tests for the Validator class.
 */
public class ValidatorTests {
  private static final Object NON_NULL = new Object();
  //////////////////////////////////////////////////////////////////////////////
  //
  //  POSITIVE TESTS
  //
  //////////////////////////////////////////////////////////////////////////////
  @Test
  public void testEnsureNotNull() {
    boolean returnValue = Validator.ensureNotNull(NON_NULL);
    assertTrue(returnValue);  // must always return true
  }
  @Test
  public void testEnsureNotNull2() {
    boolean returnValue = Validator.ensureNotNull(NON_NULL, NON_NULL);
    assertTrue(returnValue);  // must always return true
  }
  @Test
  public void testEnsureNotNull3() {
    boolean returnValue = Validator.ensureNotNull(NON_NULL, NON_NULL, NON_NULL);
    assertTrue(returnValue);  // must always return true
  }
  @Test
  public void testEnsureNotNull4() {
    boolean returnValue = Validator.ensureNotNull(NON_NULL, NON_NULL, NON_NULL, NON_NULL);
    assertTrue(returnValue);  // must always return true
  }
  @Test
  public void testEnsureTrue() {
    boolean returnValue = Validator.ensureTrue(true);
    assertTrue(returnValue);  // must always return true
  }
  @Test
  public void testEnsureTrueWithMessage() {
    boolean returnValue = Validator.ensureTrue(true, "some message");
    assertTrue(returnValue);  // must always return true
  }
  //////////////////////////////////////////////////////////////////////////////
  //
  //  NEGATIVE TESTS
  //
  //////////////////////////////////////////////////////////////////////////////
  @Test(expectedExceptions = {NullPointerException.class, AssertionError.class})
  public void testEnsureNotNullWithNull() {
    Validator.ensureNotNull(null);  // Should throw
  }
  @Test(expectedExceptions = {NullPointerException.class, AssertionError.class},
        dataProvider = "dataEnsureNotNull2WithNull")
  public void testEnsureNotNull2WithNull(Object param1, Object param2) {
    Validator.ensureNotNull(param1, param2);  // Should throw
  }
  @DataProvider(name = "dataEnsureNotNull2WithNull")
  public Object[][] dataEnsureNotNull2WithNull() {
    return new Object[][]{
            {null, NON_NULL},
            {NON_NULL, null}};
  }
  @Test(expectedExceptions = {NullPointerException.class, AssertionError.class},
        dataProvider = "dataEnsureNotNull3WithNull")
  public void testEnsureNotNull3WithNull(Object param1, Object param2, Object param3) {
    Validator.ensureNotNull(param1, param2, param3);  // Should throw
  }
  @DataProvider(name = "dataEnsureNotNull3WithNull")
  public Object[][] dataEnsureNotNull3WithNull() {
    return new Object[][]{
            {null, NON_NULL, NON_NULL},
            {NON_NULL, null, NON_NULL},
            {NON_NULL, NON_NULL, null}};
  }
  @Test(expectedExceptions = {NullPointerException.class, AssertionError.class},
        dataProvider = "dataEnsureNotNull4WithNull")
  public void testEnsureNotNull4WithNull(Object param1, Object param2, Object param3, Object param4) {
    Validator.ensureNotNull(param1, param2, param3, param4);  // Should throw
  }
  @DataProvider(name = "dataEnsureNotNull4WithNull")
  public Object[][] dataEnsureNotNull4WithNull() {
    return new Object[][]{
            {null, NON_NULL, NON_NULL, NON_NULL},
            {NON_NULL, null, NON_NULL, NON_NULL},
            {NON_NULL, NON_NULL, null, NON_NULL},
            {NON_NULL, NON_NULL, NON_NULL, null}};
  }
  @Test(expectedExceptions = {RuntimeException.class, AssertionError.class})
  public void testEnsureTrueWithFalse() {
    Validator.ensureTrue(false);
  }
  @Test(expectedExceptions = {RuntimeException.class, AssertionError.class})
  public void testEnsureTrueWithMessageWithFalse() {
    Validator.ensureTrue(false, "some message");
  }
  @Test
  public void testMessageContents() {
    Validator.resetErrorCount();
    String myMessage = "some test message";
    String thisMethod = ValidatorTests.class.getName() + "." + "testMessageContents(ValidatorTests.java:";
    try {
      Validator.ensureTrue(false, myMessage);
    } catch (Throwable e) {
      String caughtMessage = e.getMessage();
      assertTrue(caughtMessage.indexOf(myMessage) >= 0);
      assertTrue(caughtMessage.indexOf(thisMethod) >= 0);
      assertEquals(Validator.getErrorCount(), 1);
      Validator.resetErrorCount();
      assertEquals(Validator.getErrorCount(), 0);
    }
  }
}