/* * Copyright 2009 the original author or authors. * * Licensed 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.spockframework.tapestry; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.apache.tapestry5.ioc.AnnotationProvider; import org.apache.tapestry5.ioc.Registry; import org.apache.tapestry5.ioc.RegistryBuilder; import org.apache.tapestry5.ioc.annotations.Autobuild; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.ioc.annotations.InjectService; import org.spockframework.runtime.extension.AbstractMethodInterceptor; import org.spockframework.runtime.extension.IMethodInvocation; import org.spockframework.runtime.model.FieldInfo; import org.spockframework.runtime.model.SpecInfo; import org.spockframework.util.ReflectionUtil; import spock.lang.Shared; import spock.lang.Specification; /** * Controls creation, startup and shutdown of the Tapestry container, * and injects specifications with Tapestry-provided objects. * * @author Peter Niederwieser */ public class TapestryInterceptor extends AbstractMethodInterceptor { private final SpecInfo spec; private final Set<Class<?>> modules; private Registry registry; public TapestryInterceptor(SpecInfo spec, Set<Class<?>> modules) { this.spec = spec; this.modules = modules; } @Override public void interceptSharedInitializerMethod(IMethodInvocation invocation) throws Throwable { runBeforeRegistryCreatedMethods((Specification) invocation.getSharedInstance()); createAndStartupRegistry(); injectServices(invocation.getSharedInstance(), true); invocation.proceed(); } @Override public void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable { try { invocation.proceed(); } finally { shutdownRegistry(); } } @Override public void interceptInitializerMethod(IMethodInvocation invocation) throws Throwable { injectServices(invocation.getInstance(), false); invocation.proceed(); } private void runBeforeRegistryCreatedMethods(Specification spec) { Object returnValue; for (Method method : findAllBeforeRegistryCreatedMethods()) { returnValue = ReflectionUtil.invokeMethod(spec, method); // Return values of a type other than Registry are silently ignored. // This avoids problems in case the method unintentionally returns a // value (which is common due to Groovy's implicit return). if (returnValue instanceof Registry) registry = (Registry) returnValue; } } private void createAndStartupRegistry() { if (registry != null) return; RegistryBuilder builder = new RegistryBuilder(); builder.add(ExtensionModule.class); for (Class<?> module : modules) builder.add(module); registry = builder.build(); registry.performRegistryStartup(); } private List<Method> findAllBeforeRegistryCreatedMethods() { List<Method> methods = new ArrayList<Method>(); for (SpecInfo curr : spec.getSpecsTopToBottom()) { Method method = ReflectionUtil.getDeclaredMethodByName(curr.getReflection(), "beforeRegistryCreated"); if (method != null) { method.setAccessible(true); methods.add(method); } } return methods; } private void injectServices(Object target, boolean sharedFields) throws IllegalAccessException { for (final FieldInfo field : spec.getAllFields()) { Field rawField = field.getReflection(); if ((rawField.isAnnotationPresent(Inject.class) || ReflectionUtil.isAnnotationPresent(rawField, "javax.inject.Inject") || rawField.isAnnotationPresent(Autobuild.class)) && rawField.isAnnotationPresent(Shared.class) == sharedFields) { Object value = registry.getObject(rawField.getType(), createAnnotationProvider(field)); rawField.setAccessible(true); rawField.set(target, value); } else if (rawField.isAnnotationPresent(InjectService.class)) { String serviceName = rawField.getAnnotation(InjectService.class).value(); Object value = registry.getService(serviceName, rawField.getType()); rawField.setAccessible(true); rawField.set(target, value); } } } private AnnotationProvider createAnnotationProvider(final FieldInfo field) { return new AnnotationProvider() { public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { return field.getAnnotation(annotationClass); } }; } private void shutdownRegistry() { if (registry != null) registry.shutdown(); } }