Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HDDS-11981. Add annotation for registering feature validator based on a generic version #7603

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import javax.tools.Diagnostic;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
Expand All @@ -50,14 +51,11 @@
* META-INF/services/javax.annotation.processing.Processor file in the module's
* resources folder.
*/
@SupportedAnnotationTypes(
"org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator")
@SupportedAnnotationTypes({
"org.apache.hadoop.ozone.om.request.validation.OMClientVersionValidator",
"org.apache.hadoop.ozone.om.request.validation.OMLayoutVersionValidator"})
@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 class OmRequestFeatureValidatorProcessor extends AbstractProcessor {
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 =
Expand Down Expand Up @@ -90,8 +88,8 @@ public class RequestFeatureValidatorProcessor extends AbstractProcessor {
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";
private static final List<String> ANNOTATION_SIMPLE_NAMES = Arrays.asList("OMClientVersionValidator",
"OMLayoutVersionValidator");
public static final String ANNOTATION_PROCESSING_PHASE_PROPERTY_NAME =
"processingPhase";

Expand All @@ -104,7 +102,7 @@ public class RequestFeatureValidatorProcessor extends AbstractProcessor {
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
for (TypeElement annotation : annotations) {
if (!annotation.getSimpleName().contentEquals(ANNOTATION_SIMPLE_NAME)) {
if (!ANNOTATION_SIMPLE_NAMES.contains(annotation.getSimpleName().toString())) {
continue;
}
processElements(roundEnv.getElementsAnnotatedWith(annotation));
Expand Down Expand Up @@ -172,7 +170,7 @@ 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);
emitErrorMsg(ERROR_VALIDATOR_METHOD_HAS_TO_RETURN_OMRESPONSE + " " + elem.getSimpleName());
}
}

Expand Down Expand Up @@ -201,10 +199,6 @@ private boolean checkAndEvaluateAnnotation(
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);
}
Expand All @@ -228,11 +222,6 @@ private boolean isProcessingPhaseValue(
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,
Expand All @@ -246,24 +235,6 @@ private <T> T visit(
return entry.getValue().accept(visitor, null);
}

private static class ConditionValidator
extends SimpleAnnotationValueVisitor8<Boolean, Void> {

ConditionValidator() {
super(Boolean.TRUE);
}

@Override
public Boolean visitArray(List<? extends AnnotationValue> vals,
Void unused) {
if (vals.isEmpty()) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}

}

private static class ProcessingPhaseVisitor
extends SimpleAnnotationValueVisitor8<String, Void> {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import java.util.Set;

/**
* This class is an annotation processor that is hooked into the java compiler
* and is used to validate the RegisterValidator annotations in the
* codebase, to ensure that the annotated classes have the proper methods returning appropriate object types.
*
* 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.request.validation.RegisterValidator")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class RegisterValidatorProcessor extends AbstractProcessor {

public static final String ANNOTATION_SIMPLE_NAME = "RegisterValidator";
public static final String VERSION_CLASS_NAME = "org.apache.hadoop.ozone.Version";
public static final String REQUEST_PROCESSING_PHASE_CLASS_NAME = "org.apache.hadoop.ozone.om.request.validation" +
".RequestProcessingPhase";
public static final String APPLY_BEFORE_METHOD_NAME = "applyBefore";
public static final String REQUEST_TYPE_METHOD_NAME = "requestType";
public static final String PROCESSING_PHASE_METHOD_NAME = "processingPhase";

public static final String MAX_VERSION_NOT_FOUND_ERROR_MESSAGE = "Method " + APPLY_BEFORE_METHOD_NAME +
" returning an enum implementing " + VERSION_CLASS_NAME + " not found";
public static final String REQUEST_TYPE_NOT_FOUND_ERROR_MESSAGE = "Method " + REQUEST_TYPE_METHOD_NAME +
" returning an enum not found";
public static final String PROCESSING_PHASE_NOT_FOUND_ERROR_MESSAGE = "Method " + PROCESSING_PHASE_METHOD_NAME
+ " returning an enum implementing " + REQUEST_PROCESSING_PHASE_CLASS_NAME + " not found";

@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 boolean validateArrayMethod(ExecutableElement method, String expectedMethodName,
ElementKind expectedReturnType,
String expectedReturnClass) {
Elements elementUtils = processingEnv.getElementUtils();
Types types = processingEnv.getTypeUtils();
TypeElement expectedReturnInterface = expectedReturnClass == null || expectedReturnClass.equals("") ? null :
elementUtils.getTypeElement(expectedReturnClass);
return method.getSimpleName().toString().equals(expectedMethodName) && (expectedReturnType == null ||
TypeKind.ARRAY.equals(method.getReturnType().getKind()) &&
types.asElement(((ArrayType)method.getReturnType()).getComponentType()).getKind() == expectedReturnType) &&
(expectedReturnInterface == null ||
types.isAssignable(types.asElement(method.getReturnType()).asType(), expectedReturnInterface.asType()));
}

private boolean validateMethod(ExecutableElement method, String expectedMethodName, ElementKind expectedReturnType,
String expectedReturnClass) {
Elements elementUtils = processingEnv.getElementUtils();
Types types = processingEnv.getTypeUtils();
TypeElement expectedReturnInterface = expectedReturnClass == null || expectedReturnClass.equals("") ? null :
elementUtils.getTypeElement(expectedReturnClass);
return method.getSimpleName().toString().equals(expectedMethodName) && (expectedReturnType == null ||
types.asElement(method.getReturnType()) != null &&
types.asElement(method.getReturnType()).getKind() == expectedReturnType) &&
(expectedReturnInterface == null ||
types.isAssignable(types.asElement(method.getReturnType()).asType(), expectedReturnInterface.asType()));
}

private void processElements(Set<? extends Element> annotatedElements) {
for (Element element : annotatedElements) {
if (element.getKind().equals(ElementKind.ANNOTATION_TYPE)) {
boolean hasApplyBeforeMethod = false;
boolean hasRequestType = false;
boolean hasRequestProcessPhase = false;
for (Element enclosedElement : element.getEnclosedElements()) {
// Check if the annotation has a method called "validatorName" returning a String
if (enclosedElement instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement) enclosedElement;
hasApplyBeforeMethod = hasApplyBeforeMethod || validateMethod(method, APPLY_BEFORE_METHOD_NAME,
ElementKind.ENUM, VERSION_CLASS_NAME);
hasRequestType = hasRequestType || validateArrayMethod(method, REQUEST_TYPE_METHOD_NAME, ElementKind.ENUM,
null);
hasRequestProcessPhase = hasRequestProcessPhase || validateMethod(method, PROCESSING_PHASE_METHOD_NAME,
ElementKind.ENUM, REQUEST_PROCESSING_PHASE_CLASS_NAME);
}
}
if (!hasApplyBeforeMethod) {
emitErrorMsg(MAX_VERSION_NOT_FOUND_ERROR_MESSAGE + " for " +
element.getSimpleName().toString());
}
if (!hasRequestType) {
emitErrorMsg(REQUEST_TYPE_NOT_FOUND_ERROR_MESSAGE + " for " +
element.getSimpleName().toString());
}
if (!hasRequestProcessPhase) {
emitErrorMsg(PROCESSING_PHASE_NOT_FOUND_ERROR_MESSAGE + " for " +
element.getSimpleName().toString());
}
}
}
}


private void emitErrorMsg(String s) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, s);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.request.validation;

import org.apache.hadoop.ozone.Versioned;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
* Annotations to register a validator. {@link org.apache.ozone.annotations.RegisterValidatorProcessor}
* enforces other annotation to have the following methods:
* applyBefore : Returns an enum which implement {@link Versioned}
* requestType: Returns an Enum value.
* processingPhase: Returns {@link RequestProcessingPhase}
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface RegisterValidator {
String APPLY_BEFORE_METHOD_NAME = "applyBefore";
String REQUEST_TYPE_METHOD_NAME = "requestType";
String PROCESSING_PHASE_METHOD_NAME = "processingPhase";
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.hadoop.ozone.om.request.validation;
package org.apache.hadoop.ozone.request.validation;

/**
* Processing phase defines when a request validator should run.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 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 for any server.
* {@link org.apache.hadoop.ozone.request.validation.RegisterValidator}
* is used to register any validator which has the following methods:
* - applyBefore : Returns an enum which implement {@link org.apache.hadoop.ozone.Versioned}
* - requestType: Returns an Enum value.
* - processingPhase: Returns {@link org.apache.hadoop.ozone.request.validation.RequestProcessingPhase}
*
* The system uses a reflection based discovery to find methods that are
* annotated with the
* {@link org.apache.hadoop.ozone.request.validation.RegisterValidator}
* annotation.
* This annotation is used to register a particular annotation which inturn would be used to specify conditions 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 and the maxVersion corresponding to which this
* is supposed to run.
*/

package org.apache.hadoop.ozone.request.validation;
3 changes: 2 additions & 1 deletion hadoop-ozone/ozone-manager/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,8 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
</annotationProcessorPaths>
<annotationProcessors>
<annotationProcessor>org.apache.hadoop.hdds.conf.ConfigFileGenerator</annotationProcessor>
<annotationProcessor>org.apache.ozone.annotations.RequestFeatureValidatorProcessor</annotationProcessor>
<annotationProcessor>org.apache.ozone.annotations.OmRequestFeatureValidatorProcessor</annotationProcessor>
<annotationProcessor>org.apache.ozone.annotations.RegisterValidatorProcessor</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
import org.apache.hadoop.ozone.om.request.OMClientRequest;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
import org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator;
import org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.om.request.validation.ValidationCondition;
import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
import org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator;
import org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.om.request.validation.ValidationCondition;
import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
import org.slf4j.Logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
import org.apache.hadoop.ozone.om.helpers.BucketEncryptionKeyInfo;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
import org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator;
import org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.om.request.validation.ValidationCondition;
import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
import org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
import org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator;
import org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.om.request.validation.ValidationCondition;
import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
import org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
import org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator;
import org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.om.request.validation.ValidationCondition;
import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
import org.apache.hadoop.ozone.om.response.file.OMFileCreateResponse;
Expand Down
Loading
Loading