Author: rgodfrey
Date: Fri May 9 15:24:21 2014
New Revision: 1593562
URL: http://svn.apache.org/r1593562
Log:
QPID-5759 : [Java Broker] add annotation validation
Added:
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java
Modified:
qpid/trunk/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor
Added:
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java
URL:
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java?rev=1593562&view=auto
==============================================================================
---
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java
(added)
+++
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java
Fri May 9 15:24:21 2014
@@ -0,0 +1,327 @@
+/*
+ *
+ * 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.qpid.server.model.validation;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+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.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+
+
+@SupportedAnnotationTypes({AttributeAnnotationValidator.MANAGED_ATTRIBUTE_CLASS_NAME,
+
AttributeAnnotationValidator.DERIVED_ATTRIBUTE_CLASS_NAME,
+
AttributeAnnotationValidator.MANAGED_STATISTIC_CLASS_NAME})
+public class AttributeAnnotationValidator extends AbstractProcessor
+{
+
+ public static final String MANAGED_ATTRIBUTE_CLASS_NAME =
"org.apache.qpid.server.model.ManagedAttribute";
+ public static final String DERIVED_ATTRIBUTE_CLASS_NAME =
"org.apache.qpid.server.model.DerivedAttribute";
+
+ public static final String MANAGED_STATISTIC_CLASS_NAME =
"org.apache.qpid.server.model.ManagedStatistic";
+
+
+
+ private static final Set<TypeKind> VALID_PRIMITIVE_TYPES = new
HashSet<>(Arrays.asList(TypeKind.BOOLEAN,
+
TypeKind.BYTE,
+
TypeKind.CHAR,
+
TypeKind.DOUBLE,
+
TypeKind.FLOAT,
+
TypeKind.INT,
+
TypeKind.LONG,
+
TypeKind.SHORT));
+
+ @Override
+ public SourceVersion getSupportedSourceVersion()
+ {
+ return SourceVersion.latest();
+ }
+
+ @Override
+ public boolean process(final Set<? extends TypeElement> annotations, final
RoundEnvironment roundEnv)
+ {
+
+ processAttributes(roundEnv, MANAGED_ATTRIBUTE_CLASS_NAME);
+ processAttributes(roundEnv, DERIVED_ATTRIBUTE_CLASS_NAME);
+
+ processStatistics(roundEnv, MANAGED_STATISTIC_CLASS_NAME);
+
+ return false;
+ }
+
+ public void processAttributes(final RoundEnvironment roundEnv,
+ String elementName)
+ {
+
+ Elements elementUtils = processingEnv.getElementUtils();
+ TypeElement annotationElement =
elementUtils.getTypeElement(elementName);
+
+ for (Element e : roundEnv.getElementsAnnotatedWith(annotationElement))
+ {
+ checkAnnotationIsOnMethodInInterface(annotationElement, e);
+
+ ExecutableElement methodElement = (ExecutableElement) e;
+
+ checkInterfaceExtendsConfiguredObject(annotationElement,
methodElement);
+ checkMethodTakesNoArgs(annotationElement, methodElement);
+ checkMethodName(annotationElement, methodElement);
+ checkMethodReturnType(annotationElement, methodElement);
+
+ checkTypeAgreesWithName(annotationElement, methodElement);
+ }
+ }
+
+ public void processStatistics(final RoundEnvironment roundEnv,
+ String elementName)
+ {
+
+ Elements elementUtils = processingEnv.getElementUtils();
+ TypeElement annotationElement =
elementUtils.getTypeElement(elementName);
+
+ for (Element e : roundEnv.getElementsAnnotatedWith(annotationElement))
+ {
+ checkAnnotationIsOnMethodInInterface(annotationElement, e);
+
+ ExecutableElement methodElement = (ExecutableElement) e;
+
+ checkInterfaceExtendsConfiguredObject(annotationElement,
methodElement);
+ checkMethodTakesNoArgs(annotationElement, methodElement);
+ checkMethodName(annotationElement, methodElement);
+ checkTypeAgreesWithName(annotationElement, methodElement);
+ checkMethodReturnTypeIsNumber(annotationElement, methodElement);
+
+ }
+ }
+
+ private void checkMethodReturnTypeIsNumber(final TypeElement
annotationElement,
+ final ExecutableElement
methodElement)
+ {
+ Types typeUtils = processingEnv.getTypeUtils();
+ Elements elementUtils = processingEnv.getElementUtils();
+
+ TypeMirror numberType =
elementUtils.getTypeElement("java.lang.Number").asType();
+ if(!typeUtils.isAssignable(methodElement.getReturnType(),numberType))
+ {
+ processingEnv.getMessager()
+ .printMessage(Diagnostic.Kind.ERROR,
+ "@"
+ + annotationElement.getSimpleName()
+ + " return type does not extend Number: "
+ + methodElement.getReturnType().toString(),
+ methodElement
+ );
+ }
+ }
+
+ public void checkTypeAgreesWithName(final TypeElement annotationElement,
final ExecutableElement methodElement)
+ {
+ Types typeUtils = processingEnv.getTypeUtils();
+
+ String methodName = methodElement.getSimpleName().toString();
+
+ if((methodName.startsWith("is") || methodName.startsWith("has"))
+ && !(methodElement.getReturnType().getKind() == TypeKind.BOOLEAN
+ ||
typeUtils.isSameType(typeUtils.boxedClass(typeUtils.getPrimitiveType(TypeKind.BOOLEAN)).asType(),
methodElement.getReturnType())))
+ {
+ processingEnv.getMessager()
+ .printMessage(Diagnostic.Kind.ERROR,
+ "@"
+ + annotationElement.getSimpleName()
+ + " return type is not boolean or Boolean: "
+ + methodElement.getReturnType().toString(),
+ methodElement
+ );
+ }
+ }
+
+ public void checkMethodReturnType(final TypeElement annotationElement,
final ExecutableElement methodElement)
+ {
+ if (!isValidType(methodElement.getReturnType()))
+ {
+ processingEnv.getMessager()
+ .printMessage(Diagnostic.Kind.ERROR,
+ "@"
+ + annotationElement.getSimpleName()
+ + " cannot be applied to methods with return
type "
+ + methodElement.getReturnType().toString(),
+ methodElement
+ );
+ }
+ }
+
+ public void checkMethodName(final TypeElement annotationElement, final
ExecutableElement methodElement)
+ {
+ String methodName = methodElement.getSimpleName().toString();
+
+ if (methodName.length() < 3
+ || (methodName.length() < 4 && !methodName.startsWith("is"))
+ || !(methodName.startsWith("is") || methodName.startsWith("get")
|| methodName.startsWith("has")))
+ {
+ processingEnv.getMessager()
+ .printMessage(Diagnostic.Kind.ERROR,
+ "@"
+ + annotationElement.getSimpleName()
+ + " can only be applied to methods which of
the form getXXX(), isXXX() or hasXXX()",
+ methodElement
+ );
+ }
+ }
+
+ public void checkMethodTakesNoArgs(final TypeElement annotationElement,
final ExecutableElement methodElement)
+ {
+ if (!methodElement.getParameters().isEmpty())
+ {
+ processingEnv.getMessager()
+ .printMessage(Diagnostic.Kind.ERROR,
+ "@"
+ + annotationElement.getSimpleName()
+ + " can only be applied to methods which
take no parameters",
+ methodElement
+ );
+ }
+ }
+
+ public void checkInterfaceExtendsConfiguredObject(final TypeElement
annotationElement, final Element e)
+ {
+ Types typeUtils = processingEnv.getTypeUtils();
+ TypeMirror configuredObjectType =
getErasure("org.apache.qpid.server.model.ConfiguredObject");
+ TypeElement parent = (TypeElement) e.getEnclosingElement();
+
+
+ if (!typeUtils.isAssignable(typeUtils.erasure(parent.asType()),
configuredObjectType))
+ {
+ processingEnv.getMessager()
+ .printMessage(Diagnostic.Kind.ERROR,
+ "@"
+ + annotationElement.getSimpleName()
+ + " can only be applied to methods within an
interface which extends "
+ + configuredObjectType.toString()
+ + " which does not apply to "
+ + parent.asType().toString(),
+ e);
+ }
+ }
+
+ public void checkAnnotationIsOnMethodInInterface(final TypeElement
annotationElement, final Element e)
+ {
+ if (e.getKind() != ElementKind.METHOD ||
e.getEnclosingElement().getKind() != ElementKind.INTERFACE)
+ {
+ processingEnv.getMessager()
+ .printMessage(Diagnostic.Kind.ERROR,
+ "@"
+ + annotationElement.getSimpleName()
+ + " can only be applied to methods within an
interface",
+ e
+ );
+ }
+ }
+
+ private boolean isValidType(final TypeMirror type)
+ {
+ Types typeUtils = processingEnv.getTypeUtils();
+ Elements elementUtils = processingEnv.getElementUtils();
+ Element typeElement = typeUtils.asElement(type);
+
+ if (VALID_PRIMITIVE_TYPES.contains(type.getKind()))
+ {
+ return true;
+ }
+ for(TypeKind primitive : VALID_PRIMITIVE_TYPES)
+ {
+ if(typeUtils.isSameType(type,
typeUtils.boxedClass(typeUtils.getPrimitiveType(primitive)).asType()))
+ {
+ return true;
+ }
+ }
+ if(typeElement.getKind()==ElementKind.ENUM)
+ {
+ return true;
+ }
+
+ String className = "org.apache.qpid.server.model.ConfiguredObject";
+ TypeMirror configuredObjectType = getErasure(className);
+
+ if(typeUtils.isAssignable(typeUtils.erasure(type),
configuredObjectType))
+ {
+ return true;
+ }
+
+ if(typeUtils.isSameType(type,
elementUtils.getTypeElement("java.lang.String").asType()))
+ {
+ return true;
+ }
+
+
+
if(typeUtils.isSameType(type,elementUtils.getTypeElement("java.util.UUID").asType()))
+ {
+ return true;
+ }
+
+ TypeMirror erasedType = typeUtils.erasure(type);
+ if(typeUtils.isSameType(erasedType, getErasure("java.util.List"))
+ || typeUtils.isSameType(erasedType,
getErasure("java.util.Set"))
+ || typeUtils.isSameType(erasedType,
getErasure("java.util.Collection")))
+ {
+
+
+ for(TypeMirror paramType : ((DeclaredType)type).getTypeArguments())
+ {
+
+ if(!isValidType(paramType))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ if(typeUtils.isSameType(erasedType, getErasure("java.util.Map")))
+ {
+ List<? extends TypeMirror> args = ((DeclaredType)
type).getTypeArguments();
+ return isValidType(args.get(0)) && (isValidType(args.get(1)) ||
typeUtils.isSameType(args.get(1), getErasure("java.lang.Object")));
+ }
+
+
+ return false;
+ }
+
+ private TypeMirror getErasure(final String className)
+ {
+ final Types typeUtils = processingEnv.getTypeUtils();
+ final Elements elementUtils = processingEnv.getElementUtils();
+ return
typeUtils.erasure(elementUtils.getTypeElement(className).asType());
+ }
+
+}
Added:
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java
URL:
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java?rev=1593562&view=auto
==============================================================================
---
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java
(added)
+++
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java
Fri May 9 15:24:21 2014
@@ -0,0 +1,106 @@
+/*
+ *
+ * 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.qpid.server.model.validation;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+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.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+
+@SupportedAnnotationTypes(AttributeFieldValidation.MANAGED_ATTRIBUTE_FIELD_CLASS_NAME)
+public class AttributeFieldValidation extends AbstractProcessor
+{
+ public static final String MANAGED_ATTRIBUTE_FIELD_CLASS_NAME =
"org.apache.qpid.server.model.ManagedAttributeField";
+
+
+ @Override
+ public SourceVersion getSupportedSourceVersion()
+ {
+ return SourceVersion.latest();
+ }
+
+ @Override
+ public boolean process(final Set<? extends TypeElement> annotations, final
RoundEnvironment roundEnv)
+ {
+ Elements elementUtils = processingEnv.getElementUtils();
+ Types typeUtils = processingEnv.getTypeUtils();
+
+ TypeElement annotationElement =
elementUtils.getTypeElement(MANAGED_ATTRIBUTE_FIELD_CLASS_NAME);
+ for (Element e : roundEnv.getElementsAnnotatedWith(annotationElement))
+ {
+ for(AnnotationMirror am : e.getAnnotationMirrors())
+ {
+ if(typeUtils.isSameType(am.getAnnotationType(),
annotationElement.asType()))
+ {
+ for(Map.Entry<? extends ExecutableElement, ? extends
AnnotationValue> entry : am.getElementValues().entrySet())
+ {
+ String elementName =
entry.getKey().getSimpleName().toString();
+ if(elementName.equals("beforeSet") ||
elementName.equals("afterSet"))
+ {
+ String methodName =
entry.getValue().getValue().toString();
+ if(!"".equals(methodName))
+ {
+ TypeElement parent = (TypeElement)
e.getEnclosingElement();
+ if(!containsMethod(parent, methodName))
+ {
+
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+
"Could not find method '"
+ +
methodName
+ +
"' which is defined as the "
+ +
elementName
+ +
" action", e);
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean containsMethod(final TypeElement parent, final String
methodName)
+ {
+ for(Element element : parent.getEnclosedElements())
+ {
+ if(element.getKind().equals(ElementKind.METHOD)
+ && element.getSimpleName().toString().equals(methodName)
+ && ((ExecutableElement)element).getParameters().isEmpty())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
Modified:
qpid/trunk/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor
URL:
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor?rev=1593562&r1=1593561&r2=1593562&view=diff
==============================================================================
---
qpid/trunk/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor
(original)
+++
qpid/trunk/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor
Fri May 9 15:24:21 2014
@@ -19,3 +19,5 @@
org.apache.qpid.server.model.ConfiguredObjectFactoryGenerator
org.apache.qpid.server.plugin.PluggableProcessor
org.apache.qpid.server.model.ConfiguredObjectRegistrationGenerator
+org.apache.qpid.server.model.validation.AttributeAnnotationValidator
+org.apache.qpid.server.model.validation.AttributeFieldValidation
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]