/** * 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.openejb.assembler.classic; import junit.framework.TestCase; import javax.xml.bind.annotation.XmlTransient; import javax.xml.namespace.QName; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.URI; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Properties; import java.util.Set; /** * This test verifies that no architectural constraints have been violated * in the Info Object tree. Basically those are: * <p/> * - Only public fields of basic data types are allowed. * <p/> * So this means *no* * - methods * - constructors * - Complex data types (Class, ClassLoader, etc) * <p/> * Keeps the Info Objects inline with the concept of a basic AST (Abstract Syntax Tree) * which is produced by the ConfigurationFactory and built by the Assembler, * <p/> * See http://tomee.apache.org/configuration-and-assembly.html * * @version $Rev$ $Date$ */ public class OpenEjbConfigurationValidationTest extends TestCase { private final List<Class> seen = new ArrayList<Class>(); public void testValidate() throws Exception { seen.add(Object.class); validate(OpenEjbConfiguration.class); } private void validate(final Class clazz) throws Exception { if (clazz == null) return; if (seen.contains(clazz)) return; validate(clazz.getSuperclass()); seen.add(clazz); final String simpleName = clazz.getSimpleName(); // Constructors are not allowed in Info objects final Constructor[] constructors = clazz.getDeclaredConstructors(); assertEquals("constructors are not allowed: " + simpleName, 1, constructors.length); assertEquals("constructors are not allowed: " + simpleName, 0, constructors[0].getParameterTypes().length); // Methods are not allowed in Info objects final Method[] methods = clazz.getDeclaredMethods(); assertEquals("methods are not allowed: " + simpleName, 0, methods.length); // Annotations are not allowed in Info objects Annotation[] annotations = clazz.getDeclaredAnnotations(); assertEquals("annotations are not allowed: " + simpleName, 0, annotations.length); // We are very picky on fields as well // Only certain data types are allowed final Field[] fields = clazz.getDeclaredFields(); for (final Field field : fields) { // Only public fields are allowed assertTrue("Non-public fields are not allowed: " + simpleName + "." + field.getName(), Modifier.isPublic(field.getModifiers())); // Annotations of fields are not allowed annotations = clazz.getDeclaredAnnotations(); assertEquals("annotations are not allowed: " + simpleName + "." + field.getName(), 0, annotations.length); Class type = field.getType(); if (type.isArray()) { type = type.getComponentType(); } // All lists and collections *must* specify the generic type // No vague lists that are typeless // Guarantees that the data type in the list is one of the allowed types if (List.class.isAssignableFrom(type)) { type = getGenericType(field); assertNotNull("Lists must have a generic type: " + simpleName + "." + field.getName(), type); } // Same rules for java.util.Set collections if (Set.class.isAssignableFrom(type)) { type = getGenericType(field); assertNotNull("Sets must have a generic type: " + simpleName + "." + field.getName(), type); } // Now we check the data type of the field // Complex types are not allowed // Primitives are OK if (type.isPrimitive()) { continue; } // String is OK if (String.class.isAssignableFrom(type)) { continue; } // Properties is OK if (Properties.class.isAssignableFrom(type)) { continue; } // OK if (QName.class.isAssignableFrom(type)) { continue; } if (URI.class.isAssignableFrom(type)) { continue; } if (Date.class.isAssignableFrom(type)) { continue; } // Other InfoObjects are OK if (InfoObject.class.isAssignableFrom(type)) { validate(type); continue; } if (field.getAnnotation(XmlTransient.class) != null) { continue; } // All else is NOT allowed // Referrences to java.lang.Class or ClassLoaders, or URLs or anything else is all bad fail("Field is not of an allowed type: " + simpleName + "." + field.getName()); } } private Class getGenericType(final Field field) throws Exception { final Type genericType = field.getGenericType(); if (genericType instanceof ParameterizedType) { final ParameterizedType parameterizedType = (ParameterizedType) genericType; final Type firstParamType = parameterizedType.getActualTypeArguments()[0]; return (Class) firstParamType; } else if (genericType instanceof Class) { return (Class) genericType; } else { return null; } } }