1
2
3
4
5
6
7
8
9
10
11
12
13
14
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>**/*</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>**/*.class</code> for classes
137 * <li><code>**/*.xml</code> for XML files
138 * <li><code>**/*.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
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
211
212
213 protected void postProcessBundleContext(BundleContext context) throws Exception {
214
215
216
217 jarSettings = null;
218
219 jarSettings = getSettings();
220
221 jarCreator.setRootPath(getRootPath());
222
223 super.postProcessBundleContext(context);
224 }
225
226 }