/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2011, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.testing.junit4; import org.hibernate.testing.AfterClassOnce; import org.hibernate.testing.BeforeClassOnce; import org.hibernate.testing.OnExpectedFailure; import org.hibernate.testing.OnFailure; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.LinkedHashSet; import java.util.List; /** * Metadata about various types of callback methods on a given test class. * * @author Steve Ebersole */ public class TestClassMetadata { private static final Object[] NO_ARGS = new Object[0]; private LinkedHashSet<Method> beforeClassOnceMethods; private LinkedHashSet<Method> afterClassOnceMethods; private LinkedHashSet<Method> onFailureCallbacks; private LinkedHashSet<Method> onExpectedFailureCallbacks; public TestClassMetadata(Class testClass) { processClassHierarchy( testClass ); } private void processClassHierarchy(Class testClass) { // NOTE recursive on itself for ( Method method : testClass.getDeclaredMethods() ) { if ( method.getAnnotation( CallbackType.BEFORE_CLASS_ONCE.annotationClass ) != null ) { addBeforeClassOnceCallback( method ); } if ( method.getAnnotation( CallbackType.AFTER_CLASS_ONCE.annotationClass ) != null ) { addAfterClassOnceCallback( method ); } if ( method.getAnnotation( CallbackType.ON_FAILURE.annotationClass ) != null ) { addOnFailureCallback( method ); } if ( method.getAnnotation( CallbackType.ON_EXPECTED_FAILURE.annotationClass ) != null ) { addOnExpectedFailureCallback( method ); } } Class superClass = testClass.getSuperclass(); if ( superClass != null ) { processClassHierarchy( superClass ); } } private void addBeforeClassOnceCallback(Method method) { if ( beforeClassOnceMethods == null ) { beforeClassOnceMethods = new LinkedHashSet<Method>(); } ensureAccessibility( method ); beforeClassOnceMethods.add( method ); } private void ensureAccessibility(Method method) { if ( !method.isAccessible() ) { try { method.setAccessible( true ); } catch (Exception ignored) { // ignore for now } } } private void addAfterClassOnceCallback(Method method) { if ( afterClassOnceMethods == null ) { afterClassOnceMethods = new LinkedHashSet<Method>(); } ensureAccessibility( method ); afterClassOnceMethods.add( method ); } private void addOnFailureCallback(Method method) { if ( onFailureCallbacks == null ) { onFailureCallbacks = new LinkedHashSet<Method>(); } ensureAccessibility( method ); onFailureCallbacks.add( method ); } private void addOnExpectedFailureCallback(Method method) { if ( onExpectedFailureCallbacks == null ) { onExpectedFailureCallbacks = new LinkedHashSet<Method>(); } ensureAccessibility( method ); onExpectedFailureCallbacks.add( method ); } public void validate(List<Throwable> errors) { validate( beforeClassOnceMethods, CallbackType.BEFORE_CLASS_ONCE, errors ); validate( afterClassOnceMethods,CallbackType.AFTER_CLASS_ONCE, errors ); validate( onFailureCallbacks, CallbackType.ON_FAILURE, errors ); validate( onExpectedFailureCallbacks, CallbackType.ON_EXPECTED_FAILURE, errors ); } private void validate(LinkedHashSet<Method> callbackMethods, CallbackType callbackType, List<Throwable> errors) { if ( callbackMethods != null ) { for ( Method method : callbackMethods ) { validateCallbackMethod( method, callbackType, errors ); } } } private void validateCallbackMethod(Method method, CallbackType type, List<Throwable> errors) { if ( method.getParameterTypes().length > 0 ) { errors.add( new InvalidMethodForAnnotationException( type.buildTypeMarker() + " callback only valid on no-arg methods : " + Helper.extractMethodName( method ) ) ); } if ( !method.isAccessible() ) { try { method.setAccessible( true ); } catch (Exception e) { errors.add( new InvalidMethodForAnnotationException( type.buildTypeMarker() + " attached to inaccessible method and unable to make accessible" ) ); } } } private static enum CallbackType { BEFORE_CLASS_ONCE( BeforeClassOnce.class ), AFTER_CLASS_ONCE( AfterClassOnce.class ), ON_FAILURE( OnFailure.class ), ON_EXPECTED_FAILURE( OnExpectedFailure.class ); private final Class<? extends Annotation> annotationClass; private CallbackType(Class<? extends Annotation> annotationClass) { this.annotationClass = annotationClass; } public Class<? extends Annotation> getAnnotationClass() { return annotationClass; } public String buildTypeMarker() { return "@" + getAnnotationClass().getSimpleName(); } } public void performBeforeClassCallbacks(Object target) { performCallbacks( beforeClassOnceMethods, target ); } private void performCallbacks(LinkedHashSet<Method> callbackMethods, Object target) { if ( callbackMethods == null ) { return; } for ( Method callbackMethod : callbackMethods ) { invokeCallback( callbackMethod, target ); } } private void invokeCallback(Method callback, Object target) { try { callback.invoke( target, NO_ARGS ); } catch (InvocationTargetException e) { throw new CallbackException( callback, e.getTargetException() ); } catch (IllegalAccessException e) { throw new CallbackException( callback, e ); } } public void performAfterClassCallbacks(Object target) { performCallbacks( afterClassOnceMethods, target ); } public void performOnFailureCallback(Object target) { performCallbacks( onFailureCallbacks, target ); } public void performOnExpectedFailureCallback(Object target) { performCallbacks( onExpectedFailureCallbacks, target ); } }