You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by rg...@apache.org on 2014/05/09 17:24:22 UTC

svn commit: r1593562 - in /qpid/trunk/qpid/java/broker-codegen/src/main: java/org/apache/qpid/server/model/validation/ resources/META-INF/services/

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: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org