001/*
002 * (C) Copyright 2006-2017 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bstefanescu
018 */
019package org.nuxeo.runtime.test.runner;
020
021import java.util.Arrays;
022import java.util.HashMap;
023import java.util.HashSet;
024import java.util.Map;
025import java.util.Set;
026
027import org.junit.Rule;
028import org.junit.rules.MethodRule;
029import org.junit.runners.model.Statement;
030import org.nuxeo.common.utils.URLStreamHandlerFactoryInstaller;
031import org.nuxeo.runtime.RuntimeServiceEvent;
032import org.nuxeo.runtime.RuntimeServiceListener;
033import org.nuxeo.runtime.api.Framework;
034import org.nuxeo.runtime.model.ComponentManager;
035import org.nuxeo.runtime.test.RuntimeHarnessImpl;
036import org.nuxeo.runtime.test.runner.HotDeployer.ActionHandler;
037
038import com.google.inject.Binder;
039
040/**
041 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
042 */
043@Features({ MDCFeature.class, ConditionalIgnoreRule.Feature.class, RandomBug.Feature.class })
044public class RuntimeFeature extends SimpleFeature {
045
046    protected RuntimeHarness harness;
047
048    protected RuntimeDeployment deployment;
049
050    protected HotDeployer deployer;
051
052    /**
053     * Providers contributed by other features to override the default service provider used for a nuxeo service.
054     */
055    protected final Map<Class<?>, ServiceProvider<?>> serviceProviders;
056
057    public RuntimeFeature() {
058        serviceProviders = new HashMap<>();
059    }
060
061    public <T> void addServiceProvider(ServiceProvider<T> provider) {
062        serviceProviders.put(provider.getServiceClass(), provider);
063    }
064
065    public RuntimeHarness getHarness() {
066        return harness;
067    }
068
069    @Override
070    public void initialize(FeaturesRunner runner) throws Exception {
071        harness = new RuntimeHarnessImpl(runner.getTargetTestClass());
072        deployment = RuntimeDeployment.onTest(runner);
073        deployer = new HotDeployer(runner, harness);
074    }
075
076    public HotDeployer registerHandler(ActionHandler handler) {
077        return deployer.addHandler(handler);
078    }
079
080    public boolean unregisterHandler(ActionHandler handler) {
081        return deployer.removeHandler(handler);
082    }
083
084    @SuppressWarnings({ "unchecked", "rawtypes" })
085    @Override
086    public void configure(FeaturesRunner runner, Binder binder) {
087        binder.bind(RuntimeHarness.class).toInstance(getHarness());
088        binder.bind(HotDeployer.class).toInstance(deployer);
089        for (String svc : Framework.getRuntime().getComponentManager().getServices()) {
090            try {
091                Class clazz = Thread.currentThread().getContextClassLoader().loadClass(svc);
092                ServiceProvider<?> provider = serviceProviders.get(clazz);
093                if (provider == null) {
094                    provider = new ServiceProvider(clazz);
095                }
096                binder.bind(clazz).toProvider(provider).in(provider.getScope());
097            } catch (Exception e) {
098                throw new RuntimeException("Failed to bind service: " + svc, e);
099            }
100        }
101    }
102
103    @Override
104    public void start(final FeaturesRunner runner) throws Exception {
105        Framework.addListener(new RuntimeServiceListener() {
106
107            @Override
108            public void handleEvent(RuntimeServiceEvent event) {
109                if (event.id != RuntimeServiceEvent.RUNTIME_ABOUT_TO_START) {
110                    return;
111                }
112                Framework.removeListener(this);
113                blacklistComponents(runner);
114            }
115        });
116
117        harness.start();
118        deployment.deploy(runner, harness);
119    }
120
121    @Override
122    public void stop(FeaturesRunner runner) throws Exception {
123        harness.stop();
124    }
125
126    @Rule
127    public MethodRule onCleanupURLStreamHandlers() {
128        return (base, method, target) -> new Statement() {
129            @Override
130            public void evaluate() throws Throwable {
131                try {
132                    base.evaluate();
133                } finally {
134                    URLStreamHandlerFactoryInstaller.resetURLStreamHandlers();
135                }
136            }
137        };
138    }
139
140    @Rule
141    public MethodRule onMethodDeployment() {
142        return RuntimeDeployment.onMethod();
143    }
144
145    protected void blacklistComponents(FeaturesRunner aRunner) {
146        BlacklistComponent config = aRunner.getConfig(BlacklistComponent.class);
147        if (config.value().length == 0) {
148            return;
149        }
150        final ComponentManager manager = Framework.getRuntime().getComponentManager();
151        Set<String> blacklist = new HashSet<>(manager.getBlacklist());
152        blacklist.addAll(Arrays.asList(config.value()));
153        manager.setBlacklist(blacklist);
154    }
155
156    @Override
157    public void beforeRun(FeaturesRunner runner) throws Exception {
158        // this will make a snapshot of the component registry and will start the components
159        harness.fireFrameworkStarted();
160    }
161
162}