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

Nicolas Capponi
02.47.2013 2fef5aa0046548cb88034553f522d907195a19f7
opendj-admin/src/main/java/org/opends/server/admin/ClassPropertyDefinition.java
@@ -27,356 +27,295 @@
package org.opends.server.admin;
import static org.opends.server.util.Validator.ensureNotNull;
import static com.forgerock.opendj.util.Validator.*;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
/**
 * Class property definition.
 * <p>
 * A class property definition defines a property whose values
 * represent a Java class. It is possible to restrict the type of java
 * class by specifying "instance of" constraints.
 * A class property definition defines a property whose values represent a Java
 * class. It is possible to restrict the type of java class by specifying
 * "instance of" constraints.
 * <p>
 * Note that in a client/server environment, the client is probably
 * not capable of validating the Java class (e.g. it will not be able
 * to load it nor have access to the interfaces it is supposed to
 * implement). For this reason, it is possible to switch off
 * validation in the client by calling the static method
 * Note that in a client/server environment, the client is probably not capable
 * of validating the Java class (e.g. it will not be able to load it nor have
 * access to the interfaces it is supposed to implement). For this reason, it is
 * possible to switch off validation in the client by calling the static method
 * {@link #setAllowClassValidation(boolean)}.
 */
public final class ClassPropertyDefinition extends PropertyDefinition<String> {
  /**
   * An interface for incrementally constructing class property
   * definitions.
   */
  public static class Builder extends
      AbstractBuilder<String, ClassPropertyDefinition> {
    /**
     * An interface for incrementally constructing class property definitions.
     */
    public static class Builder extends AbstractBuilder<String, ClassPropertyDefinition> {
    // List of interfaces which property values must implement.
    private List<String> instanceOfInterfaces;
        // List of interfaces which property values must implement.
        private List<String> instanceOfInterfaces;
        // Private constructor
        private Builder(AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
            super(d, propertyName);
            this.instanceOfInterfaces = new LinkedList<String>();
        }
    // Private constructor
    private Builder(
        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
      super(d, propertyName);
        /**
         * Add an class name which property values must implement.
         *
         * @param className
         *            The name of a class which property values must implement.
         */
        public final void addInstanceOf(String className) {
            ensureNotNull(className);
      this.instanceOfInterfaces = new LinkedList<String>();
            // Do some basic checks to make sure the string representation
            // is valid.
            String value = className.trim();
            if (!value.matches(CLASS_RE)) {
                throw new IllegalArgumentException("\"" + value + "\" is not a valid Java class name");
            }
            // If possible try and load the class in order to perform
            // additional
            // validation.
            if (isAllowClassValidation()) {
                // Check that the class can be loaded so that validation can
                // be
                // performed.
                try {
                    loadClass(value);
                } catch (ClassNotFoundException e) {
                    // TODO: can we do something better here?
                    throw new RuntimeException(e);
                }
            }
            instanceOfInterfaces.add(value);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        protected ClassPropertyDefinition buildInstance(AbstractManagedObjectDefinition<?, ?> d, String propertyName,
                EnumSet<PropertyOption> options, AdministratorAction adminAction,
                DefaultBehaviorProvider<String> defaultBehavior) {
            return new ClassPropertyDefinition(d, propertyName, options, adminAction, defaultBehavior,
                    instanceOfInterfaces);
        }
    }
    // Regular expression for validating class names.
    private static final String CLASS_RE = "^([A-Za-z][A-Za-z0-9_]*\\.)*[A-Za-z][A-Za-z0-9_]*(\\$[A-Za-z0-9_]+)*$";
    // Flag indicating whether class property values should be
    // validated.
    private static boolean allowClassValidation = true;
    /**
     * Add an class name which property values must implement.
     * Create a class property definition builder.
     *
     * @param className
     *          The name of a class which property values must
     *          implement.
     * @param d
     *            The managed object definition associated with this property
     *            definition.
     * @param propertyName
     *            The property name.
     * @return Returns the new class property definition builder.
     */
    public final void addInstanceOf(String className) {
      ensureNotNull(className);
      // Do some basic checks to make sure the string representation
      // is valid.
      String value = className.trim();
      if (!value.matches(CLASS_RE)) {
        throw new IllegalArgumentException("\"" + value
            + "\" is not a valid Java class name");
      }
      // If possible try and load the class in order to perform
      // additional
      // validation.
      if (isAllowClassValidation()) {
        // Check that the class can be loaded so that validation can
        // be
        // performed.
        try {
          loadClass(value);
        } catch (ClassNotFoundException e) {
          // TODO: can we do something better here?
          throw new RuntimeException(e);
        }
      }
      instanceOfInterfaces.add(value);
    public static Builder createBuilder(AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
        return new Builder(d, propertyName);
    }
    /**
     * Determine whether or not class property definitions should validate class
     * name property values. Validation involves checking that the class exists
     * and that it implements the required interfaces.
     *
     * @return Returns <code>true</code> if class property definitions should
     *         validate class name property values.
     */
    public static boolean isAllowClassValidation() {
        return allowClassValidation;
    }
    /**
     * Specify whether or not class property definitions should validate class
     * name property values. Validation involves checking that the class exists
     * and that it implements the required interfaces.
     * <p>
     * By default validation is switched on.
     *
     * @param value
     *            <code>true</code> if class property definitions should
     *            validate class name property values.
     */
    public static void setAllowClassValidation(boolean value) {
        allowClassValidation = value;
    }
    // Load a named class.
    private static Class<?> loadClass(String className) throws ClassNotFoundException, LinkageError {
        return Class.forName(className, true, ClassLoaderProvider.getInstance().getClassLoader());
    }
    // List of interfaces which property values must implement.
    private final List<String> instanceOfInterfaces;
    // Private constructor.
    private ClassPropertyDefinition(AbstractManagedObjectDefinition<?, ?> d, String propertyName,
            EnumSet<PropertyOption> options, AdministratorAction adminAction,
            DefaultBehaviorProvider<String> defaultBehavior, List<String> instanceOfInterfaces) {
        super(d, String.class, propertyName, options, adminAction, defaultBehavior);
        this.instanceOfInterfaces = Collections.unmodifiableList(new LinkedList<String>(instanceOfInterfaces));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected ClassPropertyDefinition buildInstance(
        AbstractManagedObjectDefinition<?, ?> d,
        String propertyName, EnumSet<PropertyOption> options,
        AdministratorAction adminAction,
        DefaultBehaviorProvider<String> defaultBehavior) {
      return new ClassPropertyDefinition(d, propertyName, options,
          adminAction, defaultBehavior, instanceOfInterfaces);
    public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
        return v.visitClass(this, p);
    }
  }
  // Regular expression for validating class names.
  private static final String CLASS_RE =
    "^([A-Za-z][A-Za-z0-9_]*\\.)*[A-Za-z][A-Za-z0-9_]*(\\$[A-Za-z0-9_]+)*$";
  // Flag indicating whether class property values should be
  // validated.
  private static boolean allowClassValidation = true;
  /**
   * Create a class property definition builder.
   *
   * @param d
   *          The managed object definition associated with this
   *          property definition.
   * @param propertyName
   *          The property name.
   * @return Returns the new class property definition builder.
   */
  public static Builder createBuilder(
      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
    return new Builder(d, propertyName);
  }
  /**
   * Determine whether or not class property definitions should
   * validate class name property values. Validation involves checking
   * that the class exists and that it implements the required
   * interfaces.
   *
   * @return Returns <code>true</code> if class property definitions
   *         should validate class name property values.
   */
  public static boolean isAllowClassValidation() {
    return allowClassValidation;
  }
  /**
   * Specify whether or not class property definitions should validate
   * class name property values. Validation involves checking that the
   * class exists and that it implements the required interfaces.
   * <p>
   * By default validation is switched on.
   *
   * @param value
   *          <code>true</code> if class property definitions should
   *          validate class name property values.
   */
  public static void setAllowClassValidation(boolean value) {
    allowClassValidation = value;
  }
  // Load a named class.
  private static Class<?> loadClass(String className)
      throws ClassNotFoundException, LinkageError {
    return Class.forName(className, true, ClassLoaderProvider
        .getInstance().getClassLoader());
  }
  // List of interfaces which property values must implement.
  private final List<String> instanceOfInterfaces;
  // Private constructor.
  private ClassPropertyDefinition(
      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
      EnumSet<PropertyOption> options,
      AdministratorAction adminAction,
      DefaultBehaviorProvider<String> defaultBehavior,
      List<String> instanceOfInterfaces) {
    super(d, String.class, propertyName, options, adminAction, defaultBehavior);
    this.instanceOfInterfaces = Collections
        .unmodifiableList(new LinkedList<String>(instanceOfInterfaces));
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
    return v.visitClass(this, p);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <R, P> R accept(PropertyValueVisitor<R, P> v, String value, P p) {
    return v.visitClass(this, value, p);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String decodeValue(String value)
      throws IllegalPropertyValueStringException {
    ensureNotNull(value);
    try {
      validateValue(value);
    } catch (IllegalPropertyValueException e) {
      throw new IllegalPropertyValueStringException(this, value);
    /**
     * {@inheritDoc}
     */
    @Override
    public <R, P> R accept(PropertyValueVisitor<R, P> v, String value, P p) {
        return v.visitClass(this, value, p);
    }
    return value;
  }
    /**
     * {@inheritDoc}
     */
    @Override
    public String decodeValue(String value) throws IllegalPropertyValueStringException {
        ensureNotNull(value);
  /**
   * Get an unmodifiable list of classes which values of this property
   * must implement.
   *
   * @return Returns an unmodifiable list of classes which values of
   *         this property must implement.
   */
  public List<String> getInstanceOfInterface() {
    return instanceOfInterfaces;
  }
  /**
   * Validate and load the named class, and cast it to a subclass of
   * the specified class.
   *
   * @param <T>
   *          The requested type.
   * @param className
   *          The name of the class to validate and load.
   * @param instanceOf
   *          The class representing the requested type.
   * @return Returns the named class cast to a subclass of the
   *         specified class.
   * @throws IllegalPropertyValueException
   *           If the named class was invalid, could not be loaded, or
   *           did not implement the required interfaces.
   * @throws ClassCastException
   *           If the referenced class does not implement the
   *           requested type.
   */
  public <T> Class<? extends T> loadClass(String className,
      Class<T> instanceOf) throws IllegalPropertyValueException,
      ClassCastException {
    ensureNotNull(className, instanceOf);
    // Make sure that the named class is valid.
    validateClassName(className);
    Class<?> theClass = validateClassInterfaces(className);
    // Cast it to the required type.
    return theClass.asSubclass(instanceOf);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String normalizeValue(String value)
      throws IllegalPropertyValueException {
    ensureNotNull(value);
    return value.trim();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void validateValue(String value)
      throws IllegalPropertyValueException {
    ensureNotNull(value);
    // Always make sure the name is a valid class name.
    validateClassName(value);
    // If additional validation is enabled then attempt to load the
    // class and
    // check the interfaces that it implements/extends.
    if (allowClassValidation) {
      validateClassInterfaces(value);
    }
  }
  // Make sure that named class implements the interfaces named by
  // this
  // definition.
  private Class<?> validateClassInterfaces(String className)
      throws IllegalPropertyValueException {
    String nvalue = className.trim();
    Class<?> theClass;
    try {
      theClass = loadClass(nvalue);
    } catch (Exception e) {
      // If the class cannot be loaded then it is an invalid value.
      throw new IllegalPropertyValueException(this, className);
    }
    for (String i : instanceOfInterfaces) {
      try {
        Class<?> instanceOfClass = loadClass(i);
        if (!instanceOfClass.isAssignableFrom(theClass)) {
          throw new IllegalPropertyValueException(this, className);
        try {
            validateValue(value);
        } catch (IllegalPropertyValueException e) {
            throw new IllegalPropertyValueStringException(this, value);
        }
      } catch (Exception e) {
        // Should not happen because the class was validated when the
        // property
        // definition was constructed.
        throw new IllegalPropertyValueException(this, className);
      }
        return value;
    }
    return theClass;
  }
  // Do some basic checks to make sure the string representation is
  // valid.
  private void validateClassName(String className)
      throws IllegalPropertyValueException {
    String nvalue = className.trim();
    if (!nvalue.matches(CLASS_RE)) {
      throw new IllegalPropertyValueException(this, className);
    /**
     * Get an unmodifiable list of classes which values of this property must
     * implement.
     *
     * @return Returns an unmodifiable list of classes which values of this
     *         property must implement.
     */
    public List<String> getInstanceOfInterface() {
        return instanceOfInterfaces;
    }
  }
    /**
     * Validate and load the named class, and cast it to a subclass of the
     * specified class.
     *
     * @param <T>
     *            The requested type.
     * @param className
     *            The name of the class to validate and load.
     * @param instanceOf
     *            The class representing the requested type.
     * @return Returns the named class cast to a subclass of the specified
     *         class.
     * @throws IllegalPropertyValueException
     *             If the named class was invalid, could not be loaded, or did
     *             not implement the required interfaces.
     * @throws ClassCastException
     *             If the referenced class does not implement the requested
     *             type.
     */
    public <T> Class<? extends T> loadClass(String className, Class<T> instanceOf)
            throws IllegalPropertyValueException, ClassCastException {
        ensureNotNull(className, instanceOf);
        // Make sure that the named class is valid.
        validateClassName(className);
        Class<?> theClass = validateClassInterfaces(className);
        // Cast it to the required type.
        return theClass.asSubclass(instanceOf);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String normalizeValue(String value) throws IllegalPropertyValueException {
        ensureNotNull(value);
        return value.trim();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void validateValue(String value) throws IllegalPropertyValueException {
        ensureNotNull(value);
        // Always make sure the name is a valid class name.
        validateClassName(value);
        // If additional validation is enabled then attempt to load the
        // class and
        // check the interfaces that it implements/extends.
        if (allowClassValidation) {
            validateClassInterfaces(value);
        }
    }
    // Make sure that named class implements the interfaces named by
    // this
    // definition.
    private Class<?> validateClassInterfaces(String className) throws IllegalPropertyValueException {
        String nvalue = className.trim();
        Class<?> theClass;
        try {
            theClass = loadClass(nvalue);
        } catch (Exception e) {
            // If the class cannot be loaded then it is an invalid value.
            throw new IllegalPropertyValueException(this, className);
        }
        for (String i : instanceOfInterfaces) {
            try {
                Class<?> instanceOfClass = loadClass(i);
                if (!instanceOfClass.isAssignableFrom(theClass)) {
                    throw new IllegalPropertyValueException(this, className);
                }
            } catch (Exception e) {
                // Should not happen because the class was validated when the
                // property
                // definition was constructed.
                throw new IllegalPropertyValueException(this, className);
            }
        }
        return theClass;
    }
    // Do some basic checks to make sure the string representation is
    // valid.
    private void validateClassName(String className) throws IllegalPropertyValueException {
        String nvalue = className.trim();
        if (!nvalue.matches(CLASS_RE)) {
            throw new IllegalPropertyValueException(this, className);
        }
    }
}