/****************************************************************************** * Copyright (c) 2006, 2010 VMware Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0 * is available at http://www.opensource.org/licenses/apache2.0.php. * You may elect to redistribute this code under either of these licenses. * * Contributors: * VMware Inc. *****************************************************************************/ package org.eclipse.gemini.blueprint.test; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Properties; import org.eclipse.gemini.blueprint.test.internal.util.PropertiesUtil; import org.eclipse.gemini.blueprint.test.provisioning.ArtifactLocator; import org.eclipse.gemini.blueprint.test.provisioning.internal.LocalFileSystemMavenRepository; import org.osgi.framework.BundleContext; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** * Dependency manager class - deals with locating of various artifacts required * by the OSGi test. The artifacts are considered to be OSGi bundles that will * be installed during the OSGi platform startup. Additionally this class * installs the testing framework required bundles (such as Spring, Spring-DM). * * <p/>This implementation uses internally an {@link ArtifactLocator} to * retrieve the required dependencies for the running test. By default, the * artifact locator uses the local maven 2 repository. Maven configurations * (such as <settings.xml>) are supported. Alternatively for Maven * repositories located in non-default locations, one can use the * <code>localRepository</code> system property to specify the folder URL. * * @author Costin Leau * */ public abstract class AbstractDependencyManagerTests extends AbstractSynchronizedOsgiTests { private static final String TEST_FRRAMEWORK_BUNDLES_CONF_FILE = "/org/eclipse/gemini/blueprint/test/internal/boot-bundles.properties"; private static final String IGNORE = "ignore"; /** * Artifact locator (by default the Local Maven repository). */ private ArtifactLocator locator = new LocalFileSystemMavenRepository(); /** * * Default constructor. Constructs a new * <code>AbstractDependencyManagerTests</code> instance. * */ public AbstractDependencyManagerTests() { super(); } /** * * Constructs a new <code>AbstractDependencyManagerTests</code> instance. * * @param name test name */ public AbstractDependencyManagerTests(String name) { super(name); } private static final String GEMINI_BLUEPRINT_VERSION_PROP_KEY = "ignore.gemini.blueprint.version"; private static final String SPRING_VERSION_PROP_KEY = "ignore.spring.version"; /** uninitialised - read from the properties file */ private String springOsgiVersion = null; /** uninitialised - read from the properties file */ private String springBundledVersion = null; /** * Returns the version of the Spring-DM bundles installed by the testing * framework. * * @return Spring-DM bundles version */ protected String getSpringDMVersion() { if (springOsgiVersion == null) { springOsgiVersion = readProperty(GEMINI_BLUEPRINT_VERSION_PROP_KEY); } return springOsgiVersion; } /** * Returns the version of the Spring bundles installed by the testing * framework. * * @return Spring framework dependency version */ protected String getSpringVersion() { if (springBundledVersion == null) { springBundledVersion = readProperty(SPRING_VERSION_PROP_KEY); } return springBundledVersion; } private String readProperty(final String name) { if (System.getSecurityManager() != null) { return (String) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return System.getProperty(name); } }); } else return System.getProperty(name); } /** * Returns the bundles that have to be installed as part of the test setup. * This method provides an alternative to {@link #getTestBundles()} as it * allows subclasses to specify just the bundle name w/o worrying about * locating the artifact (which is resolved through the * {@link ArtifactLocator}). * * <p/>A bundle name can have any value and depends on the format expected * by the {@link ArtifactLocator} implementation. By default, a CSV (Comma * Separated Values) format is expected. * * <p/>This method allows a declarative approach in declaring bundles as * opposed to {@link #getTestBundles()} which provides a programmatic one. * * @return an array of testing framework bundle identifiers * @see #locateBundle(String) */ protected String[] getTestBundlesNames() { return new String[0]; } /** * Returns the bundles that have to be installed as part of the test setup. * This method is preferred as the bundles are by their names rather then as * {@link Resource}s. It allows for a <em>declarative</em> approach for * specifying bundles as opposed to {@link #getTestBundles()} which provides * a programmatic one. * * <p/>This implementation reads a predefined properties file to determine * the bundles needed. If the configuration needs to be changed, consider * changing the configuration location. * * @return an array of testing framework bundle identifiers * @see #getTestingFrameworkBundlesConfiguration() * @see #locateBundle(String) * */ protected String[] getTestFrameworkBundlesNames() { // load properties file Properties props = PropertiesUtil.loadAndExpand(getTestingFrameworkBundlesConfiguration()); if (props == null) throw new IllegalArgumentException("cannot load default configuration from " + getTestingFrameworkBundlesConfiguration()); boolean trace = logger.isTraceEnabled(); if (trace) logger.trace("Loaded properties " + props); // pass properties to test instance running inside OSGi space System.getProperties().put(GEMINI_BLUEPRINT_VERSION_PROP_KEY, props.get(GEMINI_BLUEPRINT_VERSION_PROP_KEY)); System.getProperties().put(SPRING_VERSION_PROP_KEY, props.get(SPRING_VERSION_PROP_KEY)); Properties excluded = PropertiesUtil.filterKeysStartingWith(props, IGNORE); if (trace) { logger.trace("Excluded ignored properties " + excluded); } String[] bundles = props.keySet().toArray(new String[props.size()]); // sort the array (as the Properties file doesn't respect the order) //bundles = StringUtils.sortStringArray(bundles); if (logger.isDebugEnabled()) logger.debug("Default framework bundles :" + ObjectUtils.nullSafeToString(bundles)); return bundles; } /** * Returns the location of the test framework bundles configuration. * * @return the location of the test framework bundles configuration */ protected Resource getTestingFrameworkBundlesConfiguration() { return new InputStreamResource( AbstractDependencyManagerTests.class.getResourceAsStream(TEST_FRRAMEWORK_BUNDLES_CONF_FILE)); } /** * {@inheritDoc} * * <p/>Default implementation that uses the {@link ArtifactLocator} to * resolve the bundles specified in {@link #getTestBundlesNames()}. * * Subclasses that override this method should decide whether they want to * support {@link #getTestBundlesNames()} or not. * * @see org.eclipse.gemini.blueprint.test.AbstractOsgiTests#getTestBundles() */ protected Resource[] getTestBundles() { return locateBundles(getTestBundlesNames()); } /** * {@inheritDoc} * * <p/> Default implementation that uses * {@link #getTestFrameworkBundlesNames()} to discover the bundles part of * the testing framework. * * @see org.eclipse.gemini.blueprint.test.AbstractOsgiTests#getTestFrameworkBundles() */ protected Resource[] getTestFrameworkBundles() { return locateBundles(getTestFrameworkBundlesNames()); } /** * Locates the given bundle identifiers. Will delegate to * {@link #locateBundle(String)}. * * @param bundles bundle identifiers * @return an array of Spring resources for the given bundle indentifiers */ protected Resource[] locateBundles(String[] bundles) { if (bundles == null) bundles = new String[0]; Resource[] res = new Resource[bundles.length]; for (int i = 0; i < bundles.length; i++) { res[i] = locateBundle(bundles[i]); } return res; } // Set log4j property to avoid TCCL problems during startup /** * {@inheritDoc} * * <p/>Sets specific log4j property to avoid class loading problems during * start up related to the thread context class loader. */ protected void preProcessBundleContext(BundleContext platformBundleContext) throws Exception { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { System.setProperty("log4j.ignoreTCL", "true"); return null; } }); super.preProcessBundleContext(platformBundleContext); } /** * Locates (through the {@link ArtifactLocator}) an OSGi bundle given as a * String. * * The default implementation expects the argument to be in Comma Separated * Values (CSV) format which indicates an artifact group, id, version and * optionally the type. * * @param bundleId the bundle identifier in CSV format * @return a resource pointing to the artifact location */ protected Resource locateBundle(String bundleId) { Assert.hasText(bundleId, "bundleId should not be empty"); // parse the String String[] artifactId = StringUtils.commaDelimitedListToStringArray(bundleId); Assert.isTrue(artifactId.length >= 3, "the CSV string " + bundleId + " contains too few values"); // TODO: add a smarter mechanism which can handle 1 or 2 values CSVs for (int i = 0; i < artifactId.length; i++) { artifactId[i] = StringUtils.trimWhitespace(artifactId[i]); } ArtifactLocator aLocator = getLocator(); return (artifactId.length == 3 ? aLocator.locateArtifact(artifactId[0], artifactId[1], artifactId[2]) : aLocator.locateArtifact(artifactId[0], artifactId[1], artifactId[2], artifactId[3])); } /** * Returns the ArtifactLocator used by this test suite. Subclasses should * override this method if the default locator (searching the local * projects, falling back to the Maven2 repository) is not enough. * * <p> * <b>Note</b>: This method will be used each time a bundle has to be * retrieved; it is highly recommended to return a cached instance instead * of a new one each time. * * @return artifact locator used by this test. */ protected ArtifactLocator getLocator() { return locator; } }