diff --git a/dev-support/annotations/pom.xml b/dev-support/annotations/pom.xml
new file mode 100644
index 000000000000..e65400285b3c
--- /dev/null
+++ b/dev-support/annotations/pom.xml
@@ -0,0 +1,114 @@
+
+
+
+ * http://www.apache.org/licenses/LICENSE-2.0 + *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.ozone.annotations;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import javax.tools.Diagnostic;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * This class is an annotation processor that is hooked into the java compiler
+ * and is used to validate the RequestFeatureValidator annotations in the
+ * codebase, to ensure that the annotated methods have the proper signature and
+ * return type.
+ *
+ * The module is compiled in a different execution via Maven before anything
+ * else is compiled, and then javac picks this class up as an annotation
+ * processor from the classpath via a ServiceLoader, based on the
+ * META-INF/services/javax.annotation.processing.Processor file in the module's
+ * resources folder.
+ */
+@SupportedAnnotationTypes(
+ "org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator")
+@SupportedSourceVersion(SourceVersion.RELEASE_8)
+public class RequestFeatureValidatorProcessor extends AbstractProcessor {
+
+ public static final String ERROR_CONDITION_IS_EMPTY =
+ "RequestFeatureValidator has an empty condition list. Please define the"
+ + " ValidationCondition in which the validator has to be applied.";
+ public static final String ERROR_ANNOTATED_ELEMENT_IS_NOT_A_METHOD =
+ "RequestFeatureValidator annotation is not applied to a method.";
+ public static final String ERROR_VALIDATOR_METHOD_HAS_TO_BE_STATIC =
+ "Only static methods can be annotated with the RequestFeatureValidator"
+ + " annotation.";
+ public static final String ERROR_UNEXPECTED_PARAMETER_COUNT =
+ "Unexpected parameter count. Expected: %d; found: %d.";
+ public static final String ERROR_VALIDATOR_METHOD_HAS_TO_RETURN_OMREQUEST =
+ "Pre-processing validator methods annotated with RequestFeatureValidator"
+ + " annotation has to return an OMRequest object.";
+ public static final String ERROR_VALIDATOR_METHOD_HAS_TO_RETURN_OMRESPONSE =
+ "Post-processing validator methods annotated with RequestFeatureValidator"
+ + " annotation has to return an OMResponse object.";
+ public static final String ERROR_FIRST_PARAM_HAS_TO_BE_OMREQUEST =
+ "First parameter of a RequestFeatureValidator method has to be an"
+ + " OMRequest object.";
+ public static final String ERROR_LAST_PARAM_HAS_TO_BE_VALIDATION_CONTEXT =
+ "Last parameter of a RequestFeatureValidator method has to be"
+ + " ValidationContext object.";
+ public static final String ERROR_SECOND_PARAM_HAS_TO_BE_OMRESPONSE =
+ "Second parameter of a RequestFeatureValidator method has to be an"
+ + " OMResponse object.";
+
+ public static final String OM_REQUEST_CLASS_NAME =
+ "org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos"
+ + ".OMRequest";
+ public static final String OM_RESPONSE_CLASS_NAME =
+ "org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos"
+ + ".OMResponse";
+ public static final String VALIDATION_CONTEXT_CLASS_NAME =
+ "org.apache.hadoop.ozone.om.request.validation.ValidationContext";
+
+ public static final String ANNOTATION_SIMPLE_NAME = "RequestFeatureValidator";
+ public static final String ANNOTATION_CONDITIONS_PROPERTY_NAME = "conditions";
+ public static final String ANNOTATION_PROCESSING_PHASE_PROPERTY_NAME =
+ "processingPhase";
+
+ public static final String PROCESSING_PHASE_PRE_PROCESS = "PRE_PROCESS";
+ public static final String PROCESSING_PHASE_POST_PROCESS = "POST_PROCESS";
+ public static final String ERROR_NO_PROCESSING_PHASE_DEFINED =
+ "RequestFeatureValidator has an invalid ProcessingPhase defined.";
+
+ @Override
+ public boolean process(Set extends TypeElement> annotations,
+ RoundEnvironment roundEnv) {
+ for (TypeElement annotation : annotations) {
+ if (!annotation.getSimpleName().contentEquals(ANNOTATION_SIMPLE_NAME)) {
+ continue;
+ }
+ processElements(roundEnv.getElementsAnnotatedWith(annotation));
+ }
+ return false;
+ }
+
+ private void processElements(Set extends Element> annotatedElements) {
+ for (Element elem : annotatedElements) {
+ for (AnnotationMirror methodAnnotation : elem.getAnnotationMirrors()) {
+ validateAnnotatedMethod(elem, methodAnnotation);
+ }
+ }
+ }
+
+ private void validateAnnotatedMethod(
+ Element elem, AnnotationMirror methodAnnotation) {
+ boolean isPreprocessor = checkAndEvaluateAnnotation(methodAnnotation);
+
+ checkMethodIsAnnotated(elem);
+ ensureAnnotatedMethodIsStatic(elem);
+ ensurePreProcessorReturnsOMReqest((ExecutableElement) elem, isPreprocessor);
+ ensurePostProcessorReturnsOMResponse(
+ (ExecutableElement) elem, isPreprocessor);
+ ensureMethodParameters(elem, isPreprocessor);
+ }
+
+ private void ensureMethodParameters(Element elem, boolean isPreprocessor) {
+ List extends TypeMirror> paramTypes =
+ ((ExecutableType) elem.asType()).getParameterTypes();
+ ensureParameterCount(isPreprocessor, paramTypes);
+ ensureParameterRequirements(paramTypes, 0, OM_REQUEST_CLASS_NAME,
+ ERROR_FIRST_PARAM_HAS_TO_BE_OMREQUEST);
+ if (!isPreprocessor) {
+ ensureParameterRequirements(paramTypes, 1, OM_RESPONSE_CLASS_NAME,
+ ERROR_SECOND_PARAM_HAS_TO_BE_OMRESPONSE);
+ }
+ int contextOrder = isPreprocessor ? 1 : 2;
+ ensureParameterRequirements(paramTypes, contextOrder,
+ VALIDATION_CONTEXT_CLASS_NAME,
+ ERROR_LAST_PARAM_HAS_TO_BE_VALIDATION_CONTEXT);
+ }
+
+ private void ensureParameterCount(boolean isPreprocessor,
+ List extends TypeMirror> paramTypes) {
+ int realParamCount = paramTypes.size();
+ int expectedParamCount = isPreprocessor ? 2 : 3;
+ if (realParamCount != expectedParamCount) {
+ emitErrorMsg(String.format(ERROR_UNEXPECTED_PARAMETER_COUNT,
+ expectedParamCount, realParamCount));
+ }
+ }
+
+ private void ensureParameterRequirements(
+ List extends TypeMirror> paramTypes,
+ int index, String validationContextClassName,
+ String errorLastParamHasToBeValidationContext) {
+ if (paramTypes.size() >= index + 1 &&
+ !paramTypes.get(index).toString().equals(validationContextClassName)) {
+ emitErrorMsg(errorLastParamHasToBeValidationContext);
+ }
+ }
+
+ private void ensurePostProcessorReturnsOMResponse(
+ ExecutableElement elem, boolean isPreprocessor) {
+ if (!isPreprocessor && !elem.getReturnType().toString()
+ .equals(OM_RESPONSE_CLASS_NAME)) {
+ emitErrorMsg(ERROR_VALIDATOR_METHOD_HAS_TO_RETURN_OMRESPONSE);
+ }
+ }
+
+ private void ensurePreProcessorReturnsOMReqest(
+ ExecutableElement elem, boolean isPreprocessor) {
+ if (isPreprocessor && !elem.getReturnType().toString()
+ .equals(OM_REQUEST_CLASS_NAME)) {
+ emitErrorMsg(ERROR_VALIDATOR_METHOD_HAS_TO_RETURN_OMREQUEST);
+ }
+ }
+
+ private void ensureAnnotatedMethodIsStatic(Element elem) {
+ if (!elem.getModifiers().contains(Modifier.STATIC)) {
+ emitErrorMsg(ERROR_VALIDATOR_METHOD_HAS_TO_BE_STATIC);
+ }
+ }
+
+ private void checkMethodIsAnnotated(Element elem) {
+ if (elem.getKind() != ElementKind.METHOD) {
+ emitErrorMsg(ERROR_ANNOTATED_ELEMENT_IS_NOT_A_METHOD);
+ }
+ }
+
+ private boolean checkAndEvaluateAnnotation(
+ AnnotationMirror methodAnnotation) {
+ boolean isPreprocessor = false;
+ for (Entry extends ExecutableElement, ? extends AnnotationValue>
+ entry : methodAnnotation.getElementValues().entrySet()) {
+
+ if (hasInvalidValidationCondition(entry)) {
+ emitErrorMsg(ERROR_CONDITION_IS_EMPTY);
+ }
+ if (isProcessingPhaseValue(entry)) {
+ isPreprocessor = evaluateProcessingPhase(entry);
+ }
+ }
+ return isPreprocessor;
+ }
+
+ private boolean evaluateProcessingPhase(
+ Entry extends ExecutableElement, ? extends AnnotationValue> entry) {
+ String procPhase = visit(entry, new ProcessingPhaseVisitor());
+ if (procPhase.equals(PROCESSING_PHASE_PRE_PROCESS)) {
+ return true;
+ } else if (procPhase.equals(PROCESSING_PHASE_POST_PROCESS)) {
+ return false;
+ }
+ return false;
+ }
+
+ private boolean isProcessingPhaseValue(
+ Entry extends ExecutableElement, ? extends AnnotationValue> entry) {
+ return isPropertyNamedAs(entry, ANNOTATION_PROCESSING_PHASE_PROPERTY_NAME);
+ }
+
+ private boolean hasInvalidValidationCondition(
+ Entry extends ExecutableElement, ? extends AnnotationValue> entry) {
+ return isPropertyNamedAs(entry, ANNOTATION_CONDITIONS_PROPERTY_NAME)
+ && !visit(entry, new ConditionValidator());
+ }
+
+ private boolean isPropertyNamedAs(
+ Entry extends ExecutableElement, ? extends AnnotationValue> entry,
+ String simpleName) {
+ return entry.getKey().getSimpleName().contentEquals(simpleName);
+ }
+
+ private
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation;
+
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation to mark methods that do certain request validations.
+ *
+ * The methods annotated with this annotation are collected by the
+ * {@link ValidatorRegistry} class during the initialization of the server.
+ *
+ * The conditions specify the specific use case in which the validator should be
+ * applied to the request. See {@link ValidationCondition} for more details
+ * on the specific conditions.
+ * The validator method should be applied to just one specific request type
+ * to help keep these methods simple and straightforward. If you want to use
+ * the same validation for different request types, use inheritance, and
+ * annotate the override method that just calls super.
+ * Note that the aim is to have these validators together with the request
+ * processing code, so the handling of these specific situations are easy to
+ * find.
+ *
+ * The annotated methods have to have a fixed signature.
+ * A {@link RequestProcessingPhase#PRE_PROCESS} phase method is running before
+ * the request is processed by the regular code.
+ * Its signature has to be the following:
+ * - it has to be static and idempotent
+ * - it has to have two parameters
+ * - the first parameter it is an
+ * {@link
+ * org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest}
+ * - the second parameter of type {@link ValidationContext}
+ * - the method has to return the modified request, or throw a ServiceException
+ * in case the request is considered to be invalid
+ * - the method does not need to care about preserving the request it gets,
+ * the original request is captured and saved by the calling environment.
+ *
+ * A {@link RequestProcessingPhase#POST_PROCESS} phase method is running once
+ * the
+ * {@link
+ * org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse}
+ * is calculated for a given request.
+ * Its signature has to be the following:
+ * - it has to be static and idempotent
+ * - it has three parameters
+ * - similalry to the pre-processing validators, first parameter is the
+ * OMRequest, the second parameter is the OMResponse, and the third
+ * parameter is a ValidationContext.
+ * - the method has to return the modified OMResponse or throw a
+ * ServiceException if the request is considered invalid based on response.
+ * - the method gets the request object that was supplied for the general
+ * request processing code, not the original request, while it gets a copy
+ * of the original response object provided by the general request processing
+ * code.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequestFeatureValidator {
+
+ /**
+ * Runtime conditions in which a validator should run.
+ * @return a list of conditions when the validator should be applied
+ */
+ ValidationCondition[] conditions();
+
+ /**
+ * Defines if the validation has to run before or after the general request
+ * processing.
+ * @return if this is a pre or post processing validator
+ */
+ RequestProcessingPhase processingPhase();
+
+ /**
+ * The type of the request handled by this validator method.
+ * @return the requestType to whihc the validator shoudl be applied
+ */
+ Type requestType();
+
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/RequestProcessingPhase.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/RequestProcessingPhase.java
new file mode 100644
index 000000000000..672156842914
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/RequestProcessingPhase.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation;
+
+/**
+ * Processing phase defines when a request validator should run.
+ *
+ * There are two hooking point at the moment, before and after the generic
+ * request processing code.
+ */
+public enum RequestProcessingPhase {
+ PRE_PROCESS,
+ POST_PROCESS
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/RequestValidations.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/RequestValidations.java
new file mode 100644
index 000000000000..fe34b746094f
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/RequestValidations.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation;
+
+import com.google.protobuf.ServiceException;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase.POST_PROCESS;
+import static org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase.PRE_PROCESS;
+
+/**
+ * Main class to configure and set up and access the request/response
+ * validation framework.
+ */
+public class RequestValidations {
+
+ static final Logger LOG = LoggerFactory.getLogger(RequestValidations.class);
+ private static final String DEFAULT_PACKAGE = "org.apache.hadoop.ozone";
+
+ private String validationsPackageName = DEFAULT_PACKAGE;
+ private ValidationContext context = null;
+ private ValidatorRegistry registry = null;
+
+ public synchronized RequestValidations fromPackage(String packageName) {
+ validationsPackageName = packageName;
+ return this;
+ }
+
+ public RequestValidations withinContext(ValidationContext validationContext) {
+ this.context = validationContext;
+ return this;
+ }
+
+ public synchronized RequestValidations load() {
+ registry = new ValidatorRegistry(validationsPackageName);
+ return this;
+ }
+
+ public OMRequest validateRequest(OMRequest request) throws ServiceException {
+ List
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation;
+
+import org.apache.hadoop.ozone.ClientVersion;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+
+/**
+ * Defines conditions for which validators can be assigned to.
+ *
+ * These conditions describe a situation where special request handling might
+ * be necessary. In these cases we do not override the actual request handling
+ * code, but based on certain request properties we might reject a request
+ * early, or we might modify the request, or the response received/sent from/to
+ * the client.
+ */
+public enum ValidationCondition {
+ /**
+ * Classifies validations that has to run after an upgrade until the cluster
+ * is in a pre-finalized state.
+ */
+ CLUSTER_NEEDS_FINALIZATION {
+ @Override
+ public boolean shouldApply(OMRequest req, ValidationContext ctx) {
+ return ctx.versionManager().needsFinalization();
+ }
+ },
+
+ /**
+ * Classifies validations that has to run, when the client uses an older
+ * protocol version than the server.
+ */
+ OLDER_CLIENT_REQUESTS {
+ @Override
+ public boolean shouldApply(OMRequest req, ValidationContext ctx) {
+ return req.getVersion() < ClientVersion.CURRENT_VERSION;
+ }
+ };
+
+ public abstract boolean shouldApply(OMRequest req, ValidationContext ctx);
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/ValidationContext.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/ValidationContext.java
new file mode 100644
index 000000000000..510d4336983a
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/ValidationContext.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation;
+
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+import org.apache.hadoop.ozone.upgrade.LayoutVersionManager;
+
+/**
+ * A context that contains useful information for request validator instances.
+ */
+@InterfaceStability.Evolving
+public interface ValidationContext {
+
+ /**
+ * Gets the {@link LayoutVersionManager} of the service, so that a pre
+ * finalization validation can check if the layout version it belongs to
+ * is finalized already or not.
+ *
+ * @return the {@link LayoutVersionManager} of the service
+ */
+ LayoutVersionManager versionManager();
+
+ /**
+ * Creates a context object based on the given parameters.
+ *
+ * @param versionManager the {@link LayoutVersionManager} of the service
+ * @return the {@link ValidationContext} specified by the parameters.
+ */
+ static ValidationContext of(LayoutVersionManager versionManager) {
+
+ return new ValidationContext() {
+ @Override
+ public LayoutVersionManager versionManager() {
+ return versionManager;
+ }
+ };
+ }
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/ValidatorRegistry.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/ValidatorRegistry.java
new file mode 100644
index 000000000000..72bd0bbfc631
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/ValidatorRegistry.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation;
+
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
+import org.reflections.Reflections;
+import org.reflections.scanners.MethodAnnotationsScanner;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
+
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase.POST_PROCESS;
+import static org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase.PRE_PROCESS;
+
+/**
+ * Registry that loads and stores the request validators to be applied by
+ * a service.
+ */
+public class ValidatorRegistry {
+
+ private final EnumMap
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/**
+ * Request's feature validation handling.
+ *
+ * This package holds facilities to add new situation specific behaviour to
+ * request handling without cluttering the basic logic of the request handler
+ * code.
+ *
+ * Typical use case scenarios, that we had in mind during the design:
+ * - during an upgrade, in the pre-finalized state certain request types are
+ * to be rejected based on provided properties of the request not based on the
+ * request type
+ * - a client connects to the server but uses an older version of the protocol
+ * - a client connects to the server but uses a newer version of the protocol
+ * - the code can handle certain checks that have to run all the time, but at
+ * first we do not see a general use case that we would pull in immediately.
+ * These are the current
+ * {@link org.apache.hadoop.ozone.om.request.validation.ValidationCondition}s
+ * but this list might be extended later on if we see other use cases.
+ *
+ * The system uses a reflection based discovery to find methods that are
+ * annotated with the
+ * {@link org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator}
+ * annotation.
+ * This annotation is used to specify the condition in which a certain validator
+ * has to be used, the request type to which the validation should be applied,
+ * and the request processing phase in which we apply the validation.
+ *
+ * One validator can be applied in multiple
+ * {@link org.apache.hadoop.ozone.om.request.validation.ValidationCondition}
+ * but a validator has to handle strictly just one
+ * {@link org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type
+ * }.
+ * The main reason to avoid validating multiple request types with the same
+ * validator, is that these validators have to be simple methods without state
+ * any complex validation has to happen in the reql request handling.
+ * In these validators we need to ensure that in the given condition the request
+ * is rejected with a proper message, or rewritten to the proper format if for
+ * example we want to handle an old request with a new server, but we need some
+ * additional values set to something default, while in the meantime we want to
+ * add meaning to a null value from newer clients.
+ *
+ * In general, it is a good practice to have the request handling code, and the
+ * validations tied together in one class.
+ */
+package org.apache.hadoop.ozone.om.request.validation;
\ No newline at end of file
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java
index ef8206937ec8..06aa6c546a3b 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java
@@ -38,6 +38,8 @@
import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer.RaftServerStatus;
import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
import org.apache.hadoop.ozone.om.request.OMClientRequest;
+import org.apache.hadoop.ozone.om.request.validation.RequestValidations;
+import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
@@ -60,6 +62,9 @@ public class OzoneManagerProtocolServerSideTranslatorPB implements
OzoneManagerProtocolPB {
private static final Logger LOG = LoggerFactory
.getLogger(OzoneManagerProtocolServerSideTranslatorPB.class);
+ private static final String OM_REQUESTS_PACKAGE =
+ "org.apache.hadoop.ozone";
+
private final OzoneManagerRatisServer omRatisServer;
private final RequestHandler handler;
private final boolean isRatisEnabled;
@@ -68,6 +73,7 @@ public class OzoneManagerProtocolServerSideTranslatorPB implements
private final AtomicLong transactionIndex;
private final OzoneProtocolMessageDispatcher
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
+import org.apache.ozone.annotations.RequestFeatureValidatorProcessor;
+import org.junit.Test;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+import static org.apache.ozone.annotations.RequestFeatureValidatorProcessor.ERROR_CONDITION_IS_EMPTY;
+import static org.apache.ozone.annotations.RequestFeatureValidatorProcessor.ERROR_FIRST_PARAM_HAS_TO_BE_OMREQUEST;
+import static org.apache.ozone.annotations.RequestFeatureValidatorProcessor.ERROR_LAST_PARAM_HAS_TO_BE_VALIDATION_CONTEXT;
+import static org.apache.ozone.annotations.RequestFeatureValidatorProcessor.ERROR_SECOND_PARAM_HAS_TO_BE_OMRESPONSE;
+import static org.apache.ozone.annotations.RequestFeatureValidatorProcessor.ERROR_UNEXPECTED_PARAMETER_COUNT;
+import static org.apache.ozone.annotations.RequestFeatureValidatorProcessor.ERROR_VALIDATOR_METHOD_HAS_TO_BE_STATIC;
+import static org.apache.ozone.annotations.RequestFeatureValidatorProcessor.ERROR_VALIDATOR_METHOD_HAS_TO_RETURN_OMREQUEST;
+import static org.apache.ozone.annotations.RequestFeatureValidatorProcessor.ERROR_VALIDATOR_METHOD_HAS_TO_RETURN_OMRESPONSE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Compile tests against the annotation processor for the
+ * {@link RequestFeatureValidator} annotation.
+ *
+ * The processor should ensure the method signatures and return values, based
+ * on annotation arguments provided.
+ */
+public class TestRequestFeatureValidatorProcessor {
+
+ private static final String CLASSNAME = "Validation";
+
+ @Test
+ public void testAnnotationCanOnlyBeAppliedOnMethods() {
+ Class
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation;
+
+import com.google.protobuf.ServiceException;
+import org.apache.hadoop.ozone.ClientVersion;
+import org.apache.hadoop.ozone.om.request.validation.testvalidatorset1.GeneralValidatorsForTesting;
+import org.apache.hadoop.ozone.om.request.validation.testvalidatorset1.GeneralValidatorsForTesting.ValidationListener;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
+import org.apache.hadoop.ozone.upgrade.LayoutVersionManager;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.hadoop.ozone.om.request.validation.ValidationContext.of;
+import static org.apache.hadoop.ozone.om.request.validation.testvalidatorset1.GeneralValidatorsForTesting.startValidatorTest;
+import static org.apache.hadoop.ozone.om.request.validation.testvalidatorset1.GeneralValidatorsForTesting.finishValidatorTest;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.OK;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.CreateKey;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.DeleteKeys;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.RenameKey;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Testing the RequestValidations class that is used to run the validation for
+ * any given request that arrives to OzoneManager.
+ */
+public class TestRequestValidations {
+ private static final String PACKAGE =
+ "org.apache.hadoop.ozone.om.request.validation.testvalidatorset1";
+
+ private static final String PACKAGE_WO_VALIDATORS =
+ "org.apache.hadoop.hdds.annotation";
+
+ private final ValidationListenerImpl validationListener =
+ new ValidationListenerImpl();
+
+ @Before
+ public void setup() {
+ startValidatorTest();
+ validationListener.attach();
+ }
+
+ @After
+ public void tearDown() {
+ validationListener.detach();
+ finishValidatorTest();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testUsingRegistryWithoutLoading() throws ServiceException {
+ new RequestValidations()
+ .fromPackage(PACKAGE)
+ .withinContext(of(aFinalizedVersionManager()))
+ .validateRequest(aCreateKeyRequest(currentClientVersion()));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testUsingRegistryWithoutContext() throws ServiceException {
+ new RequestValidations()
+ .fromPackage(PACKAGE)
+ .load()
+ .validateRequest(aCreateKeyRequest(currentClientVersion()));
+ }
+
+ @Test
+ public void testUsingRegistryWithoutPackage() throws ServiceException {
+ new RequestValidations()
+ .withinContext(of(aFinalizedVersionManager()))
+ .load()
+ .validateRequest(aCreateKeyRequest(currentClientVersion()));
+
+ validationListener.assertNumOfEvents(0);
+ }
+
+ @Test
+ public void testNoPreValidationsWithoutValidationMethods()
+ throws ServiceException {
+ int omVersion = 0;
+ ValidationContext ctx = of(aFinalizedVersionManager());
+ RequestValidations validations = loadEmptyValidations(ctx);
+
+ validations.validateRequest(aCreateKeyRequest(omVersion));
+
+ validationListener.assertNumOfEvents(0);
+ }
+
+ @Test
+ public void testNoPostValidationsWithoutValidationMethods()
+ throws ServiceException {
+ ValidationContext ctx = of(aFinalizedVersionManager());
+ RequestValidations validations = loadEmptyValidations(ctx);
+
+ validations.validateResponse(
+ aCreateKeyRequest(currentClientVersion()), aCreateKeyResponse());
+
+ validationListener.assertNumOfEvents(0);
+ }
+
+ @Test
+ public void testNoPreValidationsRunningForRequestTypeWithoutValidators()
+ throws ServiceException {
+ ValidationContext ctx = of(aFinalizedVersionManager());
+ RequestValidations validations = loadValidations(ctx);
+
+ validations.validateRequest(aRenameKeyRequest(currentClientVersion()));
+
+ validationListener.assertNumOfEvents(0);
+ }
+
+ @Test
+ public void testNoPostValidationsAreRunningForRequestTypeWithoutValidators()
+ throws ServiceException {
+ ValidationContext ctx = of(aFinalizedVersionManager());
+ RequestValidations validations = loadValidations(ctx);
+
+ validations.validateResponse(
+ aRenameKeyRequest(currentClientVersion()), aRenameKeyResponse());
+
+ validationListener.assertNumOfEvents(0);
+ }
+
+ @Test
+ public void testPreProcessorExceptionHandling() {
+ ValidationContext ctx = of(aFinalizedVersionManager());
+ RequestValidations validations = loadValidations(ctx);
+
+ try {
+ validations.validateRequest(aDeleteKeysRequest(olderClientVersion()));
+ fail("ServiceException was expected but was not thrown.");
+ } catch (ServiceException ignored) { }
+
+ validationListener.assertNumOfEvents(1);
+ validationListener.assertExactListOfValidatorsCalled(
+ "throwingPreProcessValidator");
+ }
+
+ @Test
+ public void testPostProcessorExceptionHandling() {
+ ValidationContext ctx = of(aFinalizedVersionManager());
+ RequestValidations validations = loadValidations(ctx);
+
+ try {
+ validations.validateResponse(
+ aDeleteKeysRequest(olderClientVersion()), aDeleteKeysResponse());
+ fail("ServiceException was expected but was not thrown.");
+ } catch (ServiceException ignored) { }
+
+ validationListener.assertNumOfEvents(1);
+ validationListener.assertExactListOfValidatorsCalled(
+ "throwingPostProcessValidator");
+ }
+
+ @Test
+ public void testOldClientConditionIsRecognizedAndPreValidatorsApplied()
+ throws ServiceException {
+ ValidationContext ctx = of(aFinalizedVersionManager());
+ RequestValidations validations = loadValidations(ctx);
+
+ validations.validateRequest(aCreateKeyRequest(olderClientVersion()));
+
+ validationListener.assertNumOfEvents(1);
+ validationListener.assertExactListOfValidatorsCalled(
+ "oldClientPreProcessCreateKeyValidator");
+ }
+
+ @Test
+ public void testOldClientConditionIsRecognizedAndPostValidatorsApplied()
+ throws ServiceException {
+ ValidationContext ctx = of(aFinalizedVersionManager());
+ RequestValidations validations = loadValidations(ctx);
+
+ validations.validateResponse(
+ aCreateKeyRequest(olderClientVersion()), aCreateKeyResponse());
+
+ validationListener.assertNumOfEvents(2);
+ validationListener.assertExactListOfValidatorsCalled(
+ "oldClientPostProcessCreateKeyValidator",
+ "oldClientPostProcessCreateKeyValidator2");
+ }
+
+ @Test
+ public void testPreFinalizedWithOldClientConditionPreProcValidatorsApplied()
+ throws ServiceException {
+ ValidationContext ctx = of(anUnfinalizedVersionManager());
+ RequestValidations validations = loadValidations(ctx);
+
+ validations.validateRequest(aCreateKeyRequest(olderClientVersion()));
+
+ validationListener.assertNumOfEvents(2);
+ validationListener.assertExactListOfValidatorsCalled(
+ "preFinalizePreProcessCreateKeyValidator",
+ "oldClientPreProcessCreateKeyValidator");
+ }
+
+ @Test
+ public void testPreFinalizedWithOldClientConditionPostProcValidatorsApplied()
+ throws ServiceException {
+ ValidationContext ctx = of(anUnfinalizedVersionManager());
+ RequestValidations validations = loadValidations(ctx);
+
+ validations.validateResponse(
+ aCreateKeyRequest(olderClientVersion()), aCreateKeyResponse());
+
+ validationListener.assertNumOfEvents(3);
+ validationListener.assertExactListOfValidatorsCalled(
+ "preFinalizePostProcessCreateKeyValidator",
+ "oldClientPostProcessCreateKeyValidator",
+ "oldClientPostProcessCreateKeyValidator2");
+ }
+
+ private RequestValidations loadValidations(ValidationContext ctx) {
+ return new RequestValidations()
+ .fromPackage(PACKAGE)
+ .withinContext(ctx)
+ .load();
+ }
+
+ private RequestValidations loadEmptyValidations(ValidationContext ctx) {
+ return new RequestValidations()
+ .fromPackage(PACKAGE_WO_VALIDATORS)
+ .withinContext(ctx)
+ .load();
+ }
+
+ private int olderClientVersion() {
+ return ClientVersion.CURRENT_VERSION - 1;
+ }
+
+ private int currentClientVersion() {
+ return ClientVersion.CURRENT_VERSION;
+ }
+
+ private OMRequest aCreateKeyRequest(int clientVersion) {
+ return aRequest(CreateKey, clientVersion);
+ }
+
+ private OMRequest aDeleteKeysRequest(int clientVersion) {
+ return aRequest(DeleteKeys, clientVersion);
+ }
+
+ private OMRequest aRenameKeyRequest(int clientVersion) {
+ return aRequest(RenameKey, clientVersion);
+ }
+
+ private OMRequest aRequest(Type type, int clientVersion) {
+ return OMRequest.newBuilder()
+ .setVersion(clientVersion)
+ .setCmdType(type)
+ .setClientId("TestClient")
+ .build();
+ }
+
+ private OMResponse aCreateKeyResponse() {
+ return aResponse(CreateKey);
+ }
+
+ private OMResponse aDeleteKeysResponse() {
+ return aResponse(DeleteKeys);
+ }
+
+ private OMResponse aRenameKeyResponse() {
+ return aResponse(RenameKey);
+ }
+
+ private OMResponse aResponse(Type type) {
+ return OMResponse.newBuilder()
+ .setCmdType(type)
+ .setStatus(OK)
+ .build();
+ }
+
+ private LayoutVersionManager aFinalizedVersionManager() {
+ LayoutVersionManager vm = mock(LayoutVersionManager.class);
+ when(vm.needsFinalization()).thenReturn(false);
+ return vm;
+ }
+
+ private LayoutVersionManager anUnfinalizedVersionManager() {
+ LayoutVersionManager vm = mock(LayoutVersionManager.class);
+ when(vm.needsFinalization()).thenReturn(true);
+ return vm;
+ }
+
+ private static class ValidationListenerImpl implements ValidationListener {
+ private List
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.reflections.util.ClasspathHelper;
+
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase.POST_PROCESS;
+import static org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase.PRE_PROCESS;
+import static org.apache.hadoop.ozone.om.request.validation.ValidationCondition.CLUSTER_NEEDS_FINALIZATION;
+import static org.apache.hadoop.ozone.om.request.validation.ValidationCondition.OLDER_CLIENT_REQUESTS;
+import static org.apache.hadoop.ozone.om.request.validation.testvalidatorset1.GeneralValidatorsForTesting.startValidatorTest;
+import static org.apache.hadoop.ozone.om.request.validation.testvalidatorset1.GeneralValidatorsForTesting.finishValidatorTest;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.CreateDirectory;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.CreateKey;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.CreateVolume;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Validator registry tests.
+ * For validator method declarations see the GeneralValidatorsForTesting
+ * and ValidatorsForOnlyNewClientValidations (in ../avalidation2) classes.
+ */
+public class TestValidatorRegistry {
+ private static final String PACKAGE =
+ "org.apache.hadoop.ozone.om.request.validation.testvalidatorset1";
+
+ private static final String PACKAGE2 =
+ "org.apache.hadoop.ozone.om.request.validation.testvalidatorset2";
+
+ private static final String PACKAGE_WO_VALIDATORS =
+ "org.apache.hadoop.hdds.annotation";
+
+ @Before
+ public void setup() {
+ startValidatorTest();
+ }
+
+ @After
+ public void tearDown() {
+ finishValidatorTest();
+ }
+
+ @Test
+ public void testNoValidatorsReturnedForEmptyConditionList() {
+ ValidatorRegistry registry = new ValidatorRegistry(PACKAGE);
+ List
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation.testvalidatorset1;
+
+import org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator;
+import org.apache.hadoop.ozone.om.request.validation.TestRequestValidations;
+import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase.POST_PROCESS;
+import static org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase.PRE_PROCESS;
+import static org.apache.hadoop.ozone.om.request.validation.ValidationCondition.CLUSTER_NEEDS_FINALIZATION;
+import static org.apache.hadoop.ozone.om.request.validation.ValidationCondition.OLDER_CLIENT_REQUESTS;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.CreateKey;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.CreateVolume;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.DeleteKeys;
+
+/**
+ * Some annotated request validator method, and facilities to help check if
+ * validations were properly called from tests where applicable.
+ */
+public final class GeneralValidatorsForTesting {
+
+ /**
+ * As the validators written here does not override any request or response
+ * but throw exceptions for specific tests, a test that wants to directly
+ * use a validator here, has to turn on this boolean, and the method that
+ * the test relies on has to check for this value.
+ *
+ * This is necessary to do not affect other tests that are testing requests
+ * processing, as for some of those tests this package is on the classpath
+ * and therefore the annotated validations are loadede for them.
+ */
+ private static boolean validatorTestsRunning = false;
+
+ public static void startValidatorTest() {
+ validatorTestsRunning = true;
+ }
+
+ public static void finishValidatorTest() {
+ validatorTestsRunning = false;
+ }
+
+ private GeneralValidatorsForTesting() { }
+
+ /**
+ * Interface to easily add listeners that get notified if a certain validator
+ * method defined in this class was called.
+ *
+ * @see TestRequestValidations for more details on how this intercace is
+ * being used.
+ */
+ @FunctionalInterface
+ public interface ValidationListener {
+ void validationCalled(String calledMethodName);
+ }
+
+ private static List
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.request.validation.testvalidatorset2;
+
+import org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator;
+import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+
+import static org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase.PRE_PROCESS;
+import static org.apache.hadoop.ozone.om.request.validation.ValidationCondition.OLDER_CLIENT_REQUESTS;
+import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.CreateKey;
+
+/**
+ * Separate validator methods for a few specific tests that covers cases where
+ * there are almost no validators added.
+ */
+public final class ValidatorsForOnlyOldClientValidations {
+
+ private ValidatorsForOnlyOldClientValidations() { }
+
+ @RequestFeatureValidator(
+ conditions = { OLDER_CLIENT_REQUESTS },
+ processingPhase = PRE_PROCESS,
+ requestType = CreateKey)
+ public static OMRequest oldClientPreProcessCreateKeyValidator2(
+ OMRequest req, ValidationContext ctx) {
+ return req;
+ }
+}
diff --git a/pom.xml b/pom.xml
index d8a6252e4f77..2d1ef2c52d4a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,6 +24,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs