/* * 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.junit; import org.apache.openejb.AppContext; import org.apache.openejb.BeanContext; import org.apache.openejb.BeanType; import org.apache.openejb.ModuleContext; import org.apache.openejb.SystemException; import org.apache.openejb.core.ThreadContext; import org.apache.openejb.core.ivm.naming.IvmContext; import org.apache.openejb.core.transaction.JtaTransactionPolicyFactory; import org.apache.openejb.core.transaction.TransactionPolicy; import org.apache.openejb.core.transaction.TransactionType; import org.apache.openejb.loader.SystemInstance; import org.junit.internal.runners.model.ReflectiveCallable; import org.junit.internal.runners.statements.Fail; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; import javax.interceptor.Interceptors; import javax.transaction.TransactionManager; import java.lang.annotation.Annotation; import java.lang.reflect.Method; /** * @version $Rev$ $Date$ */ public class LocalClientRunner extends BlockJUnit4ClassRunner { private final BeanContext deployment; private final Class<?> clazz; public LocalClientRunner(final Class<?> clazz) throws InitializationError { super(clazz); deployment = createDeployment(clazz); this.clazz = clazz; } @Override protected Statement methodBlock(final FrameworkMethod method) { final Object instance = newTestInstance(); final Test test = new Test(clazz, method.getMethod(), instance, deployment); Statement statement = methodInvoker(method, instance); statement = wrap(test, statement, RunAs.class, javax.annotation.security.RunAs.class); statement = wrap(test, statement, RunTestAs.class, org.apache.openejb.junit.RunTestAs.class); statement = wrap(test, statement, Transaction.class, org.apache.openejb.junit.Transaction.class); statement = wrap(test, statement, TransactionAttribute.class, javax.ejb.TransactionAttribute.class); statement = possiblyExpectingExceptions(method, instance, statement); statement = withPotentialTimeout(method, instance, statement); statement = withBefores(method, instance, statement); statement = withAfters(method, instance, statement); return statement; } private Statement wrap(final Test test, final Statement statement, final Class<? extends AnnotationStatement> clazz, final Class<? extends Annotation> annotation) { if (test.has(annotation)) { try { final Class[] types = {annotation, Statement.class, Test.class}; final Object[] args = {test.get(annotation), statement, test}; return clazz.getConstructor(types).newInstance(args); } catch (final Exception e) { throw new IllegalStateException("Cannot construct " + clazz, e); } } return statement; } /** * Creates a new test instance * * @return new instance */ private Object newTestInstance() { try { return new ReflectiveCallable() { @Override protected Object runReflectiveCall() throws Throwable { return createTest(); } }.run(); } catch (final Throwable e) { return new Fail(e); } } private BeanContext createDeployment(final Class<?> testClass) { try { final AppContext appContext = new AppContext("", SystemInstance.get(), testClass.getClassLoader(), new IvmContext(), new IvmContext(), false); final ModuleContext moduleContext = new ModuleContext("", null, "", appContext, new IvmContext(), null); return new BeanContext(null, new IvmContext(), moduleContext, testClass, null, null, null, null, null, null, null, null, null, BeanType.MANAGED, false, false); } catch (final SystemException e) { throw new IllegalStateException(e); } } public abstract static class AnnotationStatement<A extends Annotation> extends Statement { protected final A annotation; protected final Statement next; protected final Test test; protected final BeanContext info; protected AnnotationStatement(final A annotation, final Statement next, final Test test) { this.annotation = annotation; this.next = next; this.test = test; this.info = test.info; } } private static final class Test { public final Class clazz; public final Method method; public final Object instance; public final BeanContext info; private Test(final Class clazz, final Method method, final Object instance, final BeanContext info) { this.clazz = clazz; this.method = method; this.instance = instance; this.info = info; } private <A extends Annotation> boolean has(final Class<A> a) { return method.isAnnotationPresent(a) || clazz.isAnnotationPresent(a); } private <A extends Annotation> A get(final Class<A> annotationClass) { A annotation = method.getAnnotation(annotationClass); if (annotation == null) { annotation = (A) clazz.getAnnotation(annotationClass); } return annotation; } } public static class RunAs extends AnnotationStatement<javax.annotation.security.RunAs> { public RunAs(final javax.annotation.security.RunAs annotation, final Statement next, final Test test) { super(annotation, next, test); } public void evaluate() throws Throwable { info.setRunAs(annotation.value()); final ThreadContext context = new ThreadContext(info, null); final ThreadContext old = ThreadContext.enter(context); try { next.evaluate(); } finally { ThreadContext.exit(old); } } } public static class RunTestAs extends AnnotationStatement<org.apache.openejb.junit.RunTestAs> { public RunTestAs(final org.apache.openejb.junit.RunTestAs annotation, final Statement next, final Test test) { super(annotation, next, test); } public void evaluate() throws Throwable { info.setRunAs(annotation.value()); final ThreadContext context = new ThreadContext(info, null); final ThreadContext old = ThreadContext.enter(context); try { next.evaluate(); } finally { ThreadContext.exit(old); } } } public static class TransactionAttribute extends AnnotationStatement<javax.ejb.TransactionAttribute> { public TransactionAttribute(final javax.ejb.TransactionAttribute annotation, final Statement next, final Test test) { super(annotation, next, test); } public void evaluate() throws Throwable { final TransactionManager transactionManager = SystemInstance.get().getComponent(TransactionManager.class); final JtaTransactionPolicyFactory factory = new JtaTransactionPolicyFactory(transactionManager); final TransactionType transactionType = TransactionType.get(annotation.value()); // This creates *and* begins the transaction final TransactionPolicy policy = factory.createTransactionPolicy(transactionType); try { next.evaluate(); } catch (final Throwable t) { if (!isApplicationException(t)) { policy.setRollbackOnly(); } } finally { policy.commit(); } } private boolean isApplicationException(final Throwable t) { if (t.getClass().isAnnotationPresent(javax.ejb.ApplicationException.class)) { return true; } if (t instanceof Error) { return false; } if (t instanceof RuntimeException) { return false; } return true; } } public static class Transaction extends AnnotationStatement<org.apache.openejb.junit.Transaction> { public Transaction(final org.apache.openejb.junit.Transaction annotation, final Statement next, final Test test) { super(annotation, next, test); } public void evaluate() throws Throwable { final TransactionManager transactionManager = SystemInstance.get().getComponent(TransactionManager.class); final JtaTransactionPolicyFactory factory = new JtaTransactionPolicyFactory(transactionManager); // This creates *and* begins the transaction final TransactionPolicy policy = factory.createTransactionPolicy(TransactionType.RequiresNew); try { next.evaluate(); } finally { if (annotation.rollback()) { policy.setRollbackOnly(); } policy.commit(); } } } public static class Interceptorss extends AnnotationStatement<Interceptors> { public Interceptorss(final Interceptors annotation, final Statement next, final Test test) { super(annotation, next, test); } public void evaluate() throws Throwable { } } }