View Javadoc

1   /*
2    * Copyright 2006-2008 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.osgi.test;
18  
19  import java.io.InputStream;
20  import java.util.Properties;
21  
22  import org.osgi.framework.BundleContext;
23  import org.springframework.core.io.ClassPathResource;
24  import org.springframework.core.io.Resource;
25  import org.springframework.osgi.test.internal.util.IOUtils;
26  import org.springframework.osgi.test.internal.util.jar.JarCreator;
27  import org.springframework.util.StringUtils;
28  
29  /**
30   * Abstract JUnit base class that allows easy OSGi integration testing. It
31   * builds on its super classes to allow full configuration of the underlying
32   * OSGi platform implementation, of the test bundle creation (including the
33   * manifest automatic generation).
34   * 
35   * <p/>This class follows the <em>traditional</em> Spring style of integration
36   * testing in which the test simply indicates the dependencies, leaving the rest
37   * of the work to be done by its super classes. Consider the following simple
38   * example:
39   * 
40   * <pre class="code">
41   * public class SimpleOsgiTest extends AbstractConfigurableBundleCreatorTests {
42   * 
43   * 	public void testOsgiPlatformStarts() throws Exception {
44   * 		System.out.println(bundleContext.getProperty(Constants.FRAMEWORK_VENDOR));
45   * 		System.out.println(bundleContext.getProperty(Constants.FRAMEWORK_VERSION));
46   * 		System.out.println(bundleContext.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT));
47   * 	}
48   * }
49   * </pre>
50   * 
51   * <p/> The above class can be ran just like any other JUnit test. Equinox
52   * platform will be automatically started, the test will packed in an OSGi
53   * bundle (with its manifest created automatically) which will be deployed
54   * inside the OSGi platform. After running the test inside the OSGi environment,
55   * the test results (whether they are exceptions or failures) will be reported
56   * back to the running tool transparently. Please see the reference
57   * documentation for more examples, customization tips and help on how to do
58   * efficient and fast integration testing.
59   * 
60   * <p/> This class allows the test on-the-fly bundle (jar) can be configured
61   * declaratively by indicating the locations for:
62   * <ul>
63   * <li>root folder ({@value #ROOT_DIR}) - the starting point on which the
64   * resource patterns are applied</li>
65   * <li>inclusion patterns ({@value #INCLUDE_PATTERNS})- comma separated
66   * strings which identify the resources that should be included into the
67   * archive.</li>
68   * <li>manifest ({@value #MANIFEST})- the location of the manifest used for
69   * testing (if automatic generation is undesired).</li>
70   * </ul>
71   * <p/> These settings can be configured by:
72   * <ul>
73   * <li>using a properties file. By default the property name follows the
74   * pattern "[testName]-bundle.properties", (i.e. /foo/bar/SomeTest will try to
75   * load file /foo/bar/SomeTest-bundle.properties). If no properties file is
76   * found, a set of defaults will be used.</li>
77   * 
78   * <li>overriding the default getXXX methods and providing an alternative
79   * implementation.</li>
80   * </ul>
81   * 
82   * <p/>Another useful functionality inherited from
83   * {@link AbstractOnTheFlyBundleCreatorTests} class is the ability to create a
84   * manifest for the test bundle on the fly, based on the classes present in the
85   * archive.
86   * 
87   * <p/><b>Note:</b> This class is the main testing framework entry point
88   * 
89   * @author Costin Leau
90   * 
91   * @see AbstractOnTheFlyBundleCreatorTests
92   */
93  public abstract class AbstractConfigurableBundleCreatorTests extends AbstractOnTheFlyBundleCreatorTests {
94  
95  	protected static final String ROOT_DIR = "root.dir";
96  
97  	protected static final String INCLUDE_PATTERNS = "include.patterns";
98  
99  	protected static final String LIBS = "libs";
100 
101 	protected static final String MANIFEST = "manifest";
102 
103 	private static final Properties DEFAULT_SETTINGS = new Properties();
104 
105 	static {
106 		DEFAULT_SETTINGS.setProperty(ROOT_DIR, "file:./target/test-classes/");
107 		DEFAULT_SETTINGS.setProperty(INCLUDE_PATTERNS, JarCreator.EVERYTHING_PATTERN);
108 		DEFAULT_SETTINGS.setProperty(LIBS, "");
109 		DEFAULT_SETTINGS.setProperty(MANIFEST, "");
110 	}
111 
112 	/**
113 	 * Settings for the jar creation. Static as it has to be cached between test
114 	 * runs and it is being initialized once in
115 	 * {@link #postProcessBundleContext(BundleContext)}.
116 	 */
117 	private static Properties jarSettings;
118 
119 
120 	protected String getRootPath() {
121 		return jarSettings.getProperty(ROOT_DIR);
122 	}
123 
124 	/**
125 	 * {@inheritDoc}
126 	 * 
127 	 * <p/>Ant-style patterns for identifying the resources added to the jar.The
128 	 * patterns are considered from the root path when performing the search.
129 	 * 
130 	 * <p/> By default, the content pattern is <code>*&#42;/*</code> which
131 	 * includes all sources from the root. One can configure the pattern to
132 	 * include specific files by using different patterns. For example, to
133 	 * include just the classes, XML and properties files one can use the
134 	 * following patterns:
135 	 * <ol>
136 	 * <li><code>*&#42;/*.class</code> for classes
137 	 * <li><code>*&#42;/*.xml</code> for XML files
138 	 * <li><code>*&#42;/*.properties</code> for properties files
139 	 * </ol>
140 	 * 
141 	 * @return array of Ant-style pattern
142 	 */
143 	protected String[] getBundleContentPattern() {
144 		return StringUtils.commaDelimitedListToStringArray(jarSettings.getProperty(INCLUDE_PATTERNS));
145 	}
146 
147 	protected String getManifestLocation() {
148 		return jarSettings.getProperty(MANIFEST);
149 	}
150 
151 	/**
152 	 * Returns the settings location (by default, the test name; i.e.
153 	 * <code>foo.bar.SomeTest</code> will try to load
154 	 * <code>foo/bar/SomeTest-bundle.properties</code>).
155 	 * 
156 	 * @return settings location for this test
157 	 */
158 	protected String getSettingsLocation() {
159 		return getClass().getName().replace('.', '/') + "-bundle.properties";
160 	}
161 
162 	/**
163 	 * Returns the default settings used when creating the jar, in case no
164 	 * customisations have been applied. Unless the base class is used as a
165 	 * testing framework, consider using a properties file for specifying
166 	 * specific properties for a test case.
167 	 * 
168 	 * @return default settings for creating the jar
169 	 * @see #getSettingsLocation()
170 	 */
171 	protected Properties getDefaultSettings() {
172 		return DEFAULT_SETTINGS;
173 	}
174 
175 	/**
176 	 * Returns the settings used for creating this jar. This method tries to
177 	 * locate and load the settings from the location indicated by
178 	 * {@link #getSettingsLocation()}. If no file is found, the default
179 	 * settings will be used.
180 	 * 
181 	 * <p/> A non-null properties object will always be returned.
182 	 * 
183 	 * @return settings for creating the on the fly jar
184 	 * @throws Exception if loading the settings file fails
185 	 */
186 	protected Properties getSettings() throws Exception {
187 		Properties settings = new Properties(getDefaultSettings());
188 		// settings.setProperty(ROOT_DIR, getRootPath());
189 		Resource resource = new ClassPathResource(getSettingsLocation());
190 
191 		if (resource.exists()) {
192 			InputStream stream = resource.getInputStream();
193 			try {
194 				if (stream != null) {
195 					settings.load(stream);
196 					logger.debug("Loaded jar settings from " + getSettingsLocation());
197 				}
198 			}
199 			finally {
200 				IOUtils.closeStream(stream);
201 			}
202 		}
203 		else
204 			logger.info(getSettingsLocation() + " was not found; using defaults");
205 
206 		return settings;
207 	}
208 
209 	/*
210 	 * Loads the jar settings, first from the disk falling back to the default
211 	 * settings, if none is found.
212 	 */
213 	protected void postProcessBundleContext(BundleContext context) throws Exception {
214 		// hook in properties loading
215 
216 		// reset the settings (useful when running multiple tests)
217 		jarSettings = null;
218 		// load settings
219 		jarSettings = getSettings();
220 		// Somehow the JarCreator needs to get this
221 		jarCreator.setRootPath(getRootPath());
222 
223 		super.postProcessBundleContext(context);
224 	}
225 
226 }