/* * 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.tomee.embedded.junit; import org.apache.tomee.embedded.TomEEEmbeddedApplicationRunner; import org.junit.rules.MethodRule; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runner.notification.RunListener; 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.enterprise.inject.Vetoed; import java.util.List; import java.util.Properties; import java.util.concurrent.atomic.AtomicReference; /** * see org.apache.tomee.embedded.SingleInstanceRunnerTest for a sample. * idea is to reuse some part of ApplicationComposer API to get a single container for all tests in embedded mode. * * Base is to declare an @Application class which holds the model and some injections. * Note: this can be replaced setting tomee.application-composer.application property to the fully qualified name of the app. * Note: @Application classes are only searched in the same jar as the test. * * Model: * - @Configuration: programmatic properties - note injections don't work there. * - @Classes: only context value is used. * - @ContainerProperties: to configure the container * - @WebResource: first value can be used to set the docBase (other values are ignored) * - @TomEEEmbeddedSingleRunner.LifecycleTasks: allow to add some lifecycle tasks (like starting a ftp/sft/elasticsearch... server) * * Injections: * - CDI * - @RandomPort: with the value http or https. Supported types are URL (context base) and int (the port). */ @Vetoed public class TomEEEmbeddedSingleRunner extends BlockJUnit4ClassRunner { private static final AtomicReference<TomEEEmbeddedApplicationRunner> RUNNER = new AtomicReference<>(); // use when you use another runner like Parameterized of JUnit public static class Rule implements TestRule { private final Object test; public Rule(final Object test) { this.test = test; } @Override public Statement apply(final Statement base, final Description description) { return new Statement() { @Override public void evaluate() throws Throwable { start(test); RUNNER.get().composerInject(test); base.evaluate(); } }; } } public static class Start extends RunListener { @Override public void testStarted(final Description description) throws Exception { start(null); } } private static void start(final Object marker) throws Exception { getRunner().start(marker.getClass(), (Properties) null); } public static void setApp(final Object o) { getRunner().setApp(o); } private static TomEEEmbeddedApplicationRunner getRunner() { final TomEEEmbeddedApplicationRunner runner = RUNNER.get(); if (runner == null) { RUNNER.compareAndSet(null, new TomEEEmbeddedApplicationRunner()); } return RUNNER.get(); } public static void close() { final TomEEEmbeddedApplicationRunner runner = RUNNER.get(); if (runner != null) { runner.close(); } } public TomEEEmbeddedSingleRunner(final Class<?> klass) throws InitializationError { super(klass); } @Override protected List<MethodRule> rules(final Object test) { final List<MethodRule> rules = super.rules(test); rules.add(new MethodRule() { @Override public Statement apply(final Statement base, final FrameworkMethod method, final Object target) { return new Statement() { @Override public void evaluate() throws Throwable { start(test); getRunner().composerInject(target); base.evaluate(); } }; } }); return rules; } }