/****************************************************************************** * 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.io.InputStream; import java.util.Properties; import org.eclipse.gemini.blueprint.test.internal.util.IOUtils; import org.eclipse.gemini.blueprint.test.internal.util.jar.JarCreator; import org.osgi.framework.BundleContext; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.util.StringUtils; /** * Abstract JUnit base class that allows easy OSGi integration testing. It * builds on its super classes to allow full configuration of the underlying * OSGi platform implementation, of the test bundle creation (including the * manifest automatic generation). * * <p/>This class follows the <em>traditional</em> Spring style of integration * testing in which the test simply indicates the dependencies, leaving the rest * of the work to be done by its super classes. Consider the following simple * example: * * <pre class="code"> * public class SimpleOsgiTest extends AbstractConfigurableBundleCreatorTests { * * public void testOsgiPlatformStarts() throws Exception { * System.out.println(bundleContext.getProperty(Constants.FRAMEWORK_VENDOR)); * System.out.println(bundleContext.getProperty(Constants.FRAMEWORK_VERSION)); * System.out.println(bundleContext.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT)); * } * } * </pre> * * <p/> The above class can be ran just like any other JUnit test. Equinox * platform will be automatically started, the test will packed in an OSGi * bundle (with its manifest created automatically) which will be deployed * inside the OSGi platform. After running the test inside the OSGi environment, * the test results (whether they are exceptions or failures) will be reported * back to the running tool transparently. Please see the reference * documentation for more examples, customization tips and help on how to do * efficient and fast integration testing. * * <p/> This class allows the test on-the-fly bundle (jar) can be configured * declaratively by indicating the locations for: * <ul> * <li>root folder ({@value #ROOT_DIR}) - the starting point on which the * resource patterns are applied</li> * <li>inclusion patterns ({@value #INCLUDE_PATTERNS})- comma separated * strings which identify the resources that should be included into the * archive.</li> * <li>manifest ({@value #MANIFEST})- the location of the manifest used for * testing (if automatic generation is undesired).</li> * </ul> * <p/> These settings can be configured by: * <ul> * <li>using a properties file. By default the property name follows the * pattern "[testName]-bundle.properties", (i.e. /foo/bar/SomeTest will try to * load file /foo/bar/SomeTest-bundle.properties). If no properties file is * found, a set of defaults will be used.</li> * * <li>overriding the default getXXX methods and providing an alternative * implementation.</li> * </ul> * * <p/>Another useful functionality inherited from * {@link AbstractOnTheFlyBundleCreatorTests} class is the ability to create a * manifest for the test bundle on the fly, based on the classes present in the * archive. * * <p/><b>Note:</b> This class is the main testing framework entry point * * @author Costin Leau * * @see AbstractOnTheFlyBundleCreatorTests */ public abstract class AbstractConfigurableBundleCreatorTests extends AbstractOnTheFlyBundleCreatorTests { protected static final String ROOT_DIR = "root.dir"; protected static final String INCLUDE_PATTERNS = "include.patterns"; protected static final String LIBS = "libs"; protected static final String MANIFEST = "manifest"; private static final Properties DEFAULT_SETTINGS = new Properties(); static { DEFAULT_SETTINGS.setProperty(ROOT_DIR, Thread.currentThread().getContextClassLoader().getResource(".").toString()); DEFAULT_SETTINGS.setProperty(INCLUDE_PATTERNS, JarCreator.EVERYTHING_PATTERN); DEFAULT_SETTINGS.setProperty(LIBS, ""); DEFAULT_SETTINGS.setProperty(MANIFEST, ""); } /** * Settings for the jar creation. Static as it has to be cached between test * runs and it is being initialized once in * {@link #postProcessBundleContext(BundleContext)}. */ private static Properties jarSettings; protected String getRootPath() { return jarSettings.getProperty(ROOT_DIR); } /** * {@inheritDoc} * * <p/>Ant-style patterns for identifying the resources added to the jar.The * patterns are considered from the root path when performing the search. * * <p/> By default, the content pattern is <code>**/*</code> which * includes all sources from the root. One can configure the pattern to * include specific files by using different patterns. For example, to * include just the classes, XML and properties files one can use the * following patterns: * <ol> * <li><code>**/*.class</code> for classes * <li><code>**/*.xml</code> for XML files * <li><code>**/*.properties</code> for properties files * </ol> * * @return array of Ant-style pattern */ protected String[] getBundleContentPattern() { return StringUtils.commaDelimitedListToStringArray(jarSettings.getProperty(INCLUDE_PATTERNS)); } protected String getManifestLocation() { return jarSettings.getProperty(MANIFEST); } /** * Returns the settings location (by default, the test name; i.e. * <code>foo.bar.SomeTest</code> will try to load * <code>foo/bar/SomeTest-bundle.properties</code>). * * @return settings location for this test */ protected String getSettingsLocation() { return getClass().getName().replace('.', '/') + "-bundle.properties"; } /** * Returns the default settings used when creating the jar, in case no * customisations have been applied. Unless the base class is used as a * testing framework, consider using a properties file for specifying * specific properties for a test case. * * @return default settings for creating the jar * @see #getSettingsLocation() */ protected Properties getDefaultSettings() { return DEFAULT_SETTINGS; } /** * Returns the settings used for creating this jar. This method tries to * locate and load the settings from the location indicated by * {@link #getSettingsLocation()}. If no file is found, the default * settings will be used. * * <p/> A non-null properties object will always be returned. * * @return settings for creating the on the fly jar * @throws Exception if loading the settings file fails */ protected Properties getSettings() throws Exception { Properties settings = new Properties(getDefaultSettings()); // settings.setProperty(ROOT_DIR, getRootPath()); Resource resource = new ClassPathResource(getSettingsLocation()); if (resource.exists()) { InputStream stream = resource.getInputStream(); try { if (stream != null) { settings.load(stream); logger.debug("Loaded jar settings from " + getSettingsLocation()); } } finally { IOUtils.closeStream(stream); } } else logger.info(getSettingsLocation() + " was not found; using defaults"); return settings; } /* * Loads the jar settings, first from the disk falling back to the default * settings, if none is found. */ protected void postProcessBundleContext(BundleContext context) throws Exception { // hook in properties loading // reset the settings (useful when running multiple tests) jarSettings = null; // load settings jarSettings = getSettings(); // Somehow the JarCreator needs to get this jarCreator.setRootPath(getRootPath()); super.postProcessBundleContext(context); } }