/* * Copyright 2009 Toni Menzel. * * 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.ops4j.pax.exam.raw.extender.intern; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.ops4j.pax.exam.ProbeInvoker; import org.ops4j.pax.exam.TestContainerException; import org.ops4j.pax.exam.util.Injector; import org.ops4j.pax.swissbox.tracker.ServiceLookup; import org.osgi.framework.BundleContext; /** * Turns a instruction into a service call. Currently used with encoded instructions from * org.ops4j.pax.exam.spi.container.ClassMethodTestAddress * * @author Toni Menzel * @since Dec 4, 2009 */ public class ProbeInvokerImpl implements ProbeInvoker { private BundleContext ctx; private String clazz; private String method; private Injector injector; public ProbeInvokerImpl(String encodedInstruction, BundleContext bundleContext) { // parse class and method out of expression: String[] parts = encodedInstruction.split(";"); clazz = parts[0]; method = parts[1]; ctx = bundleContext; // acquire (optional) injector // TODO replace system property by core configuration option // boolean inject = "true".equals(System.getProperty( "pax.exam.inject" )); boolean inject = true; if (inject) { injector = ServiceLookup.getService(ctx, Injector.class); } else { injector = new NoOpInjector(); } } public void call(Object... args) { Class<?> testClass; try { testClass = ctx.getBundle().loadClass(clazz); } catch (ClassNotFoundException e) { throw new TestContainerException(e); } if (!(findAndInvoke(testClass, args))) { throw new TestContainerException(" Test " + method + " not found in test class " + testClass.getName()); } } private boolean findAndInvoke(Class<?> testClass, Object... params) { try { // find matching method for (Method m : testClass.getMethods()) { if (m.getName().equals(method)) { // we assume its correct: injectContextAndInvoke(testClass.newInstance(), m, params); return true; } } } catch (NoClassDefFoundError e) { throw new TestContainerException(e); } catch (InstantiationException e) { throw new TestContainerException(e); } catch (IllegalAccessException e) { throw new TestContainerException(e); } return false; } /** * Invokes the bundle context (if possible and required) and executes the regression method. * * TODO this is a trimmed down minimal version that does not support any junit before/afters or * self made injection. The only thing you get here is a parameter injection for BundleContext * types. * * @param testInstance * an instance of the regression class * @param testMethod * regression method * @param params * * @throws TestContainerException * - Re-thrown from reflection invokation */ private void injectContextAndInvoke(final Object testInstance, final Method testMethod, Object[] params) { final Class<?>[] paramTypes = testMethod.getParameterTypes(); injector.injectFields(testInstance); try { // runBefores( testInstance ); if (paramTypes.length == 0) { testMethod.invoke(testInstance); } else { Object[] parameters = injectHook(testMethod, params); testMethod.invoke(testInstance, parameters); } } catch (InvocationTargetException e) { throw new TestContainerException(e); } catch (IllegalAccessException e) { throw new TestContainerException(e); } } /** * This method practcally makes sure the method that is going to be invoked has the right types * and instances injected as parameters. * * The following rules apply: You either have no arguments. You have arguments, then * BundleContext must be your first. Parameters with @Inject Annotation must come next. All * remaining arguments are set the params values. * * @param testMethod * method in question * @param params * derived from caller. Addditional injections may apply * * @return filled parameters ready for method invokation */ private Object[] injectHook(Method testMethod, Object[] params) { // skip all injections Class<?>[] paramTypes = testMethod.getParameterTypes(); Object[] ret = new Object[paramTypes.length]; Annotation[][] paramAnnotations = testMethod.getParameterAnnotations(); int paramCursor = 0; for (int i = 0; i < ret.length; i++) { if (i == 0) { ret[0] = ctx; } else { if (paramAnnotations[i].length > 0) { // skip throw new RuntimeException("Parameter " + i + " on " + testMethod.getName() + " has Annotation. Not supported until Pax Exam 2.1"); } else { if (params.length > paramCursor) { ret[i] = params[paramCursor++]; } else { // set default to null ret[i] = null; } } } } return ret; } }