/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * bstefanescu */ package org.eclipse.ecr.testlib.runner; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.Name; import javax.naming.NameNotFoundException; import javax.naming.NamingException; import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.ecr.runtime.api.DataSourceHelper; import org.eclipse.ecr.runtime.api.Framework; import org.eclipse.ecr.testlib.NXRuntimeTestCase; import com.google.inject.Binder; import com.google.inject.Provider; import com.google.inject.Scopes; /** * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> * */ public class RuntimeFeature extends SimpleFeature { private static final Log log = LogFactory.getLog(RuntimeFeature.class); protected RuntimeHarness harness; protected final DeploymentSet deploy; /** * Providers contributed by other features to override the default service provider * used for a nuxeo service. */ protected final Map<Class<?>, Provider<?>> serviceProviders; public RuntimeFeature() { harness = new NXRuntimeTestCase(); deploy = new DeploymentSet(); serviceProviders = new HashMap<Class<?>, Provider<?>>(); } public <T> void addServiceProvider(Class<T> clazz, Provider<T> provider) { serviceProviders.put(clazz, provider); } public RuntimeHarness getHarness() { return harness; } public DeploymentSet deployments() { return deploy; } private void scanDeployments(FeaturesRunner runner) { List<RunnerFeature> features = runner.getFeatures(); if (features == null) { throw new IllegalStateException("Cannot call scanDeployments until features are not loaded"); } for (RunnerFeature feature : features) { deploy.load(FeaturesRunner.getScanner(), feature.getClass()); } // load deployments from class to run deploy.load(FeaturesRunner.getScanner(), runner.getTestClass().getJavaClass()); } public String[] getDeployments() { return deploy.getDeployments().toArray(new String[deploy.getDeployments().size()]); } public String[] getLocalDeployments() { return deploy.getLocalDeployments().toArray(new String[deploy.getLocalDeployments().size()]); } /** * Deploys bundles specified in the @Bundles annotation. */ protected void deployTestClassBundles() throws Exception { String[] bundles = getDeployments(); if (bundles.length > 0) { harness = getHarness(); for (String bundle : bundles) { try { int p = bundle.indexOf(':'); if (p == -1) { harness.deployBundle(bundle); } else { harness.deployContrib(bundle.substring(0, p), bundle.substring(p+1)); } } catch (Exception e) { log.error("Unable to deploy artifact: " + bundle, e); } } } String[] localResources = getLocalDeployments(); if (localResources.length > 0) { harness = getHarness(); for (String bundle : localResources) { try { int p = bundle.indexOf(':'); if (p == -1) { throw new IllegalArgumentException("Local resources must specify a traget bundle. "+bundle); } else { URL url = getClass().getClassLoader().getResource(bundle.substring(p+1)); harness.deployTestContrib(bundle.substring(0, p), url); } } catch (Exception e) { log.error("Unable to deploy artifact: " + bundle, e); } } } harness.fireFrameworkStarted(); } @Override public void initialize(FeaturesRunner runner) throws Exception { scanDeployments(runner); } @Override public void start(FeaturesRunner runner) throws Exception { // Starts Nuxeo Runtime if (!harness.isStarted()) { harness.start(); } // Deploy bundles deployTestClassBundles(); } @Override public void stop(FeaturesRunner runner) throws Exception { // Stops the harness if needed if (harness.isStarted()) { harness.stop(); //harness = null; } } //TODO this is not ok. we should not force 2 modules layers - we should be able to load any number of module layers. @SuppressWarnings("unchecked") @Override public void configure(FeaturesRunner runner, Binder binder) { for (String svc : Framework.getRuntime().getComponentManager().getServices()) { try { Class clazz = Class.forName(svc); Provider provider = serviceProviders.get(clazz); if (provider == null) { bind0(binder, clazz); } else { bind0(binder, clazz, provider); } } catch (Exception e) { throw new RuntimeException("Failed to bind service: "+svc, e); } } binder.bind(RuntimeHarness.class).toInstance(getHarness()); // binder.bind(FeaturesRunner.class).toInstance(runner); // binder.bind(NuxeoRunner.class).toInstance(runner); } protected <T> void bind0(Binder binder, Class<T> type) { binder.bind(type).toProvider(new ServiceProvider<T>(type)).in(Scopes.SINGLETON); } protected <T> void bind0(Binder binder, Class<T> type, Provider<T> provider) { binder.bind(type).toProvider(provider).in(Scopes.SINGLETON); } public static void bindDatasource(String key, DataSource ds) throws Exception { InitialContext initialCtx = new InitialContext(); String dsName = DataSourceHelper.getDataSourceJNDIName(key); rebind(initialCtx, dsName, ds); } public static void rebind(Context ctx, String key, Object value) throws NamingException { Name name = ctx.getNameParser("").parse(key); int depth = name.size() - 1; for (int i=0; i<depth; i++) { String segment = name.get(i); try { ctx = (Context)ctx.lookup(segment); } catch (NameNotFoundException e) { ctx = ctx.createSubcontext(segment); } } ctx.rebind(name.get(depth), value); } }