summaryrefslogtreecommitdiff
path: root/qpid/java/broker-codegen
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2014-05-09 15:24:21 +0000
committerRobert Godfrey <rgodfrey@apache.org>2014-05-09 15:24:21 +0000
commit49d68d0a1b2fb706ca8d356bb7cde5d1e6e86778 (patch)
tree043a316004946094dac83c241c0fdbf0a819340b /qpid/java/broker-codegen
parent566f96394dd04b81c3e2987a221dd935a7ab2276 (diff)
downloadqpid-python-49d68d0a1b2fb706ca8d356bb7cde5d1e6e86778.tar.gz
QPID-5759 : [Java Broker] add annotation validation
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1593562 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/broker-codegen')
-rw-r--r--qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java327
-rw-r--r--qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java106
-rw-r--r--qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor2
3 files changed, 435 insertions, 0 deletions
diff --git a/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java b/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java
new file mode 100644
index 0000000000..1422a52449
--- /dev/null
+++ b/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java
@@ -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());
+ }
+
+}
diff --git a/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java b/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java
new file mode 100644
index 0000000000..58b7191ba7
--- /dev/null
+++ b/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java
@@ -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;
+ }
+}
diff --git a/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor
index 005394d209..49964e3354 100644
--- a/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -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