/** * 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.hive; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeSet; import org.junit.*; import static org.junit.Assert.*; public class VersionTestBase { public String getParameterTypeName(Class<?> parameterType, Map<Class, String> versionedClassToNameMap) { if (versionedClassToNameMap.containsKey(parameterType)) { return versionedClassToNameMap.get(parameterType); } else { return parameterType.getSimpleName(); } } public String getMethodKey(Method method, Map<Class, String> versionedClassToNameMap) { //------------------------------------------------- StringBuilder sb = new StringBuilder(); int modifiers = method.getModifiers(); if ((modifiers & Modifier.STATIC) != 0) { sb.append("static"); } else { sb.append("non-static"); } sb.append(" "); Class<?> returnType = method.getReturnType(); sb.append(getParameterTypeName(returnType, versionedClassToNameMap)); sb.append(" "); sb.append(method.getName()); Class<?>[] parameterTypes = method.getParameterTypes(); sb.append("("); boolean isFirst = true; for (Class<?> parameterType : parameterTypes) { if (!isFirst) { sb.append(", "); } sb.append(getParameterTypeName(parameterType, versionedClassToNameMap)); isFirst = false; } sb.append(")"); Class<?>[] exceptionsThrown = method.getExceptionTypes(); if (exceptionsThrown.length > 0) { sb.append(" throws "); isFirst = true; for (Class<?> exceptionThrown : exceptionsThrown) { if (!isFirst) { sb.append(", "); } sb.append(exceptionThrown.getSimpleName()); isFirst = false; } } return sb.toString(); //------------------------------------------------- } public String getFieldKey(Field field, Map<Class, String> versionedClassToNameMap) throws IllegalAccessException { //------------------------------------------------- StringBuilder sb = new StringBuilder(); int modifiers = field.getModifiers(); if ((modifiers & Modifier.STATIC) != 0) { sb.append("static"); } else { sb.append("non-static"); } sb.append(" "); Class<?> fieldType = field.getType(); sb.append(getParameterTypeName(fieldType, versionedClassToNameMap)); sb.append(" "); sb.append(field.getName()); if ((modifiers & Modifier.STATIC) != 0) { sb.append(" "); sb.append(field.get(null)); } return sb.toString(); //------------------------------------------------- } public Method[] onlyPublicMethods(Method[] methods) { List<Method> resultList = new ArrayList<Method>(); for (Method method : methods) { if ((method.getModifiers() & Modifier.PUBLIC) != 0) { resultList.add(method); } } return resultList.toArray(new Method[0]); } public Field[] onlyPublicFields(Field[] fields) { List<Field> resultList = new ArrayList<Field>(); for (Field field : fields) { if ((field.getModifiers() & Modifier.PUBLIC) != 0) { resultList.add(field); } } return resultList.toArray(new Field[0]); } public TreeSet<String> getMethodKeySetForAnnotation(Method[] methods, Class annotationClass, Map<Class, String> versionedClassToNameMap) throws IllegalAccessException { TreeSet<String> result = new TreeSet<String>(); for (Method method : methods) { Annotation[] declaredAnnotations = method.getDeclaredAnnotations(); boolean isFound = false; for (Annotation declaredAnnotation : declaredAnnotations) { if (declaredAnnotation.annotationType().equals(annotationClass)) { isFound = true; break; } } if (!isFound) { continue; } result.add(getMethodKey(method, versionedClassToNameMap)); } return result; } public TreeSet<String> getMethodKeySetExcludingAnnotations(Method[] methods, List<Class> versionAnnotations, Map<Class, String> versionedClassToNameMap) throws IllegalAccessException { TreeSet<String> result = new TreeSet<String>(); for (Method method : methods) { Annotation[] declaredAnnotations = method.getDeclaredAnnotations(); boolean isFound = false; for (Annotation declaredAnnotation : declaredAnnotations) { for (Class versionAnnotation : versionAnnotations) { if (declaredAnnotation.annotationType().equals(versionAnnotation)) { isFound = true; break; } } if (isFound) { break; } } if (isFound) { continue; } String methodKey = getMethodKey(method, versionedClassToNameMap); if (!methodKey.equals("non-static int compareTo(Object)")) { result.add(methodKey); } } return result; } public TreeSet<String> getFieldKeySetForAnnotation(Field[] fields, Class annotationClass, Map<Class, String> versionedClassToNameMap) throws IllegalAccessException { TreeSet<String> result = new TreeSet<String>(); for (Field field : fields) { Annotation[] declaredAnnotations = field.getDeclaredAnnotations(); boolean isFound = false; for (Annotation declaredAnnotation : declaredAnnotations) { if (declaredAnnotation.annotationType().equals(annotationClass)) { isFound = true; break; } } if (!isFound) { continue; } result.add(getFieldKey(field, versionedClassToNameMap)); } return result; } public TreeSet<String> getFieldKeySetExcludingAnnotations(Field[] fields, List<Class> versionAnnotations, Map<Class, String> versionedClassToNameMap) throws IllegalAccessException { TreeSet<String> result = new TreeSet<String>(); for (Field field : fields) { Annotation[] declaredAnnotations = field.getDeclaredAnnotations(); boolean isFound = false; for (Annotation declaredAnnotation : declaredAnnotations) { for (Class versionAnnotation : versionAnnotations) { if (declaredAnnotation.annotationType().equals(versionAnnotation)) { isFound = true; break; } } if (isFound) { break; } } if (isFound) { continue; } result.add(getFieldKey(field, versionedClassToNameMap)); } return result; } // For now, olderClass has 1 version and newerClass 2 versions... public void doVerifyVersions( Class olderClass, Class olderVersionClass, Class newerClass, Class newerVersionClass, Map<Class, String> versionedClassToNameMap) throws IllegalAccessException { List<Class> olderVersionClasses = new ArrayList<Class>(); olderVersionClasses.add(olderVersionClass); List<Class> newerVersionClasses = new ArrayList<Class>(); newerVersionClasses.add(olderVersionClass); newerVersionClasses.add(newerVersionClass); //---------------------------------------------------------------------------------------------- Method[] olderMethods = onlyPublicMethods(olderClass.getDeclaredMethods()); TreeSet<String> olderMethodSet = getMethodKeySetForAnnotation(olderMethods, olderVersionClass, versionedClassToNameMap); TreeSet<String> olderNoMethodAnnotationsSet = getMethodKeySetExcludingAnnotations(olderMethods, olderVersionClasses, versionedClassToNameMap); Field[] olderFields = onlyPublicFields(olderClass.getFields()); TreeSet<String> olderFieldSet = getFieldKeySetForAnnotation(olderFields, olderVersionClass, versionedClassToNameMap); TreeSet<String> olderNoFieldAnnotationsSet = getFieldKeySetExcludingAnnotations(olderFields, olderVersionClasses, versionedClassToNameMap); //---------------------------------------------------------------------------------------------- Method[] newerMethods = onlyPublicMethods(newerClass.getDeclaredMethods()); TreeSet<String> newerMethodSetV1 = getMethodKeySetForAnnotation(newerMethods, olderVersionClass, versionedClassToNameMap); TreeSet<String> newerMethodSetV2 = getMethodKeySetForAnnotation(newerMethods, newerVersionClass, versionedClassToNameMap); TreeSet<String> newerNoMethodAnnotationsSetV1andV2 = getMethodKeySetExcludingAnnotations(newerMethods, newerVersionClasses, versionedClassToNameMap); Field[] newerFields = onlyPublicFields(newerClass.getFields()); // doDisplayFields(newerFields, newerClass); TreeSet<String> newerFieldSetV1 = getFieldKeySetForAnnotation(newerFields, olderVersionClass, versionedClassToNameMap); TreeSet<String> newerFieldSetV2 = getFieldKeySetForAnnotation(newerFields, newerVersionClass, versionedClassToNameMap); TreeSet<String> newerNoFieldAnnotationsSetV1andV2 = getFieldKeySetExcludingAnnotations(newerFields, newerVersionClasses, versionedClassToNameMap); //---------------------------------------------------------------------------------------------- // VALIDATION //---------------------------------------------------------------------------------------------- // No version annotation? if (olderNoMethodAnnotationsSet.size() != 0) { Assert.assertTrue("Class " + olderClass.getSimpleName() + " has 1 or more public methods without a version V1 annotation " + olderNoMethodAnnotationsSet.toString(), false); } if (olderNoFieldAnnotationsSet.size() != 0) { Assert.assertTrue("Class " + olderClass.getSimpleName() + " has 1 or more public fields without a version V1 annotation " + olderNoFieldAnnotationsSet.toString(), false); } if (newerNoMethodAnnotationsSetV1andV2.size() != 0) { Assert.assertTrue("Class " + newerClass.getSimpleName() + " has 1 or more public methods without a version V1 or V2 annotation " + newerNoMethodAnnotationsSetV1andV2.toString(), false); } if (newerNoFieldAnnotationsSetV1andV2.size() != 0) { Assert.assertTrue("Class " + newerClass.getSimpleName() + " has 1 or more public methods without a version V1 or V2 annotation " + newerNoFieldAnnotationsSetV1andV2.toString(), false); } // Do the V1 methods of older and newer match? if (!olderMethodSet.equals(newerMethodSetV1)) { TreeSet<String> leftCopy = new TreeSet<String>(olderMethodSet); leftCopy.removeAll(newerMethodSetV1); TreeSet<String> rightCopy = new TreeSet<String>(newerMethodSetV1); rightCopy.removeAll(olderMethodSet); Assert.assertTrue("Class " + olderClass.getSimpleName() + " and class " + newerClass.getSimpleName() + " methods are different for V1 " + leftCopy.toString() + " " + rightCopy.toString(), false); } // Do the V1 fields of older and newer match? if (!olderFieldSet.equals(newerFieldSetV1)) { TreeSet<String> leftCopy = new TreeSet<String>(olderFieldSet); leftCopy.removeAll(newerFieldSetV1); TreeSet<String> rightCopy = new TreeSet<String>(newerFieldSetV1); rightCopy.removeAll(olderFieldSet); Assert.assertTrue("Class " + olderClass.getSimpleName() + " and class " + newerClass.getSimpleName() + " fields are different for V1 " + leftCopy.toString() + " " + rightCopy.toString(), false); } } }