PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/org.amdatu.web.resourcehandler.itest/src/org/amdatu/web/resourcehandler/itest/ResourceHandlerTestBase.java

https://gitlab.com/bram1/amdatu-web
Java | 393 lines | 272 code | 51 blank | 70 comment | 47 complexity | 3d8b4af1ea6ab28404e32de151819e74 MD5 | raw file
  1. /**
  2. * Licensed under the Apache License, Version 2.0 (the "License");
  3. * you may not use this file except in compliance with the License.
  4. * You may obtain a copy of the License at
  5. *
  6. * http://www.apache.org/licenses/LICENSE-2.0
  7. *
  8. * Unless required by applicable law or agreed to in writing, software
  9. * distributed under the License is distributed on an "AS IS" BASIS,
  10. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. * See the License for the specific language governing permissions and
  12. * limitations under the License.
  13. */
  14. package org.amdatu.web.resourcehandler.itest;
  15. import java.io.IOException;
  16. import java.io.InputStream;
  17. import java.net.HttpURLConnection;
  18. import java.net.MalformedURLException;
  19. import java.net.URL;
  20. import java.util.Collections;
  21. import java.util.List;
  22. import java.util.Map;
  23. import java.util.Properties;
  24. import java.util.Scanner;
  25. import java.util.concurrent.CountDownLatch;
  26. import java.util.concurrent.TimeUnit;
  27. import org.apache.felix.dm.tracker.ServiceTracker;
  28. import org.osgi.framework.Bundle;
  29. import org.osgi.framework.BundleContext;
  30. import org.osgi.framework.BundleEvent;
  31. import org.osgi.framework.BundleListener;
  32. import org.osgi.framework.FrameworkUtil;
  33. import org.osgi.framework.ServiceRegistration;
  34. import org.osgi.service.cm.Configuration;
  35. import org.osgi.service.cm.ConfigurationAdmin;
  36. import org.osgi.service.cm.ConfigurationEvent;
  37. import org.osgi.service.cm.ConfigurationListener;
  38. import junit.framework.TestCase;
  39. public abstract class ResourceHandlerTestBase extends TestCase implements BundleListener {
  40. protected static final String RESOURCE_HANDLER_PID = "org.amdatu.web.resourcehandler";
  41. protected static final int HTTP_OK = 200;
  42. protected static final int HTTP_NOT_FOUND = 404;
  43. private static final String PREFIX = "http://localhost:8080";
  44. private static final String SLASH = "/";
  45. protected static final String URL_HELLO_WORLD = "bundle1/helloworld.txt";
  46. protected static final String URL_FAREWELL_WORLD = "bundle2/farewellworld1.txt";
  47. protected static final String URL_FAREWELL_AGAIN = "bundle2/farewellworld2.txt";
  48. protected static final String URL_FAREWELL_HELLO_WORLD = "bundle1/bundle2/farewellworld.txt";
  49. protected static final String URL_BUNDLE4_ROOT_WITHOUT_DEFAULT = "bundle4/";
  50. protected static final String URL_BUNDLE4_ROOT_WITH_DEFAULT = "bundle4/default.txt";
  51. protected static final String URL_BUNDLE4_A_WITHOUT_SLASH = "bundle4/a";
  52. protected static final String URL_BUNDLE4_A_WITHOUT_DEFAULT = "bundle4/a/";
  53. protected static final String URL_BUNDLE4_A_WITH_DEFAULT = "bundle4/a/a.txt";
  54. protected static final String URL_BUNDLE4_B_WITHOUT_DEFAULT = "bundle4/b/";
  55. protected static final String URL_BUNDLE4_B_WITH_DEFAULT = "bundle4/b/default.txt";
  56. protected static final String URL_BUNDLE4_B_NON_EXISTING = "bundle4/b/nonExisting.txt";
  57. protected static final String URL_BUNDLE4_C_WITHOUT_SLASH = "bundle4/c";
  58. protected static final String URL_BUNDLE4_C_C = "bundle4/c/c.txt";
  59. protected static final String URL_BUNDLE4_D_WITHOUT_SLASH = "bundle4/d";
  60. protected static final String URL_BUNDLE4_D_NON_EXISTING = "bundle4/d/nonExisting.txt";
  61. protected static final String URL_BUNDLE5_ROOT_WITHOUT_DEFAULT = "bundle5/";
  62. protected static final String URL_BUNDLE5_ROOT_WITH_DEFAULT = "bundle5/default.txt";
  63. protected static final String URL_WOOT = "http://localhost:8080/bundle3/woot.txt";
  64. protected static final String URL_BUNDLE1_WITHOUT_SLASH = "bundle1";
  65. protected static final String URL_BUNDLE1_WITH_SLASH = "bundle1/";
  66. protected static final String URL_BUNDLE4_WITHOUT_SLASH = "bundle4";
  67. protected static final String URL_BUNDLE4_WITH_SLASH = "bundle4/";
  68. protected static final String URL_BUNDLE6_FULL_WITHOUT_SLASH = "http://localhost:8080";
  69. protected static final String URL_BUNDLE6_FULL_WITH_SLASH = "http://localhost:8080/";
  70. protected static final String URL_BUNDLE6_WITH_SLASH = "/";
  71. protected static final String URL_BUNDLE6_WITHOUT_SLASH = "";
  72. protected static final String URL_BUNDLE6_WITH_DEFAULT = "/default.txt";
  73. protected static final String URL_BUNDLE6_X = "/x.txt";
  74. protected static final String DEFAULT_CACHE_CONTROL_VALUE = "max-age=604800, must-revalidate";
  75. protected static final String HDR_CACHE_CONTROL = "Cache-Control";
  76. protected final BundleContext m_context;
  77. private volatile BundleStateLatch m_latch;
  78. protected Bundle m_helloBundle;
  79. private Bundle m_farewellBundle;
  80. protected Bundle m_resourceHandler;
  81. protected Bundle m_bundleOwnContext;
  82. protected ResourceHandlerTestBase() {
  83. m_context = FrameworkUtil.getBundle(getClass()).getBundleContext();
  84. }
  85. /**
  86. * Provides a custom {@link CountDownLatch} that allows it to count down in
  87. * case a bundle event for a specified bundle with a specified bundle
  88. * event-type is given.
  89. */
  90. protected static class BundleStateLatch {
  91. private final CountDownLatch m_latch;
  92. private final Bundle m_bundle;
  93. private final int m_type;
  94. public BundleStateLatch(Bundle bundle, int type) {
  95. m_latch = new CountDownLatch(1);
  96. m_bundle = bundle;
  97. m_type = type;
  98. }
  99. /**
  100. * Awaits until this latch reaches a count of zero or when 5 seconds
  101. * are passed, whichever comes first.
  102. */
  103. public boolean await() throws InterruptedException {
  104. return m_latch.await(5, TimeUnit.SECONDS);
  105. }
  106. /**
  107. * Counts down in case the given bundle event matches the contained
  108. * bundle and type.
  109. *
  110. * @param event the bundle event to test, cannot be <code>null</code>.
  111. * @return <code>true</code> if the event matched, <code>false</code> otherwise.
  112. */
  113. public boolean countDownOnMatch(BundleEvent event) {
  114. if (event.getBundle() == m_bundle && event.getType() == m_type) {
  115. m_latch.countDown();
  116. return true;
  117. }
  118. return false;
  119. }
  120. }
  121. public void bundleChanged(BundleEvent event) {
  122. if (m_latch != null) {
  123. m_latch.countDownOnMatch(event);
  124. }
  125. }
  126. /**
  127. * Set up for each individual test.
  128. */
  129. @Override
  130. protected void setUp() throws Exception {
  131. Bundle[] bundles = m_context.getBundles();
  132. for (Bundle bundle : bundles) {
  133. String symbolicName = bundle.getSymbolicName();
  134. if (RESOURCE_HANDLER_PID.equals(symbolicName)) {
  135. m_resourceHandler = bundle;
  136. }
  137. if ("org.amdatu.web.resourcehandler.itest.bundle1".equals(symbolicName)) {
  138. m_helloBundle = bundle;
  139. }
  140. if ("org.amdatu.web.resourcehandler.itest.bundle2".equals(symbolicName)) {
  141. m_farewellBundle = bundle;
  142. }
  143. if ("org.amdatu.web.resourcehandler.itest.bundle3".equals(symbolicName)) {
  144. m_bundleOwnContext = bundle;
  145. }
  146. }
  147. // Sanity check before entering any test...
  148. assertNotNull("...itest.bundle1 does not appear to be installed?!", m_helloBundle);
  149. assertNotNull("...itest.bundle2 does not appear to be installed?!", m_farewellBundle);
  150. assertNotNull("...itest.bundle3 does not appear to be installed?!", m_bundleOwnContext);
  151. assertNotNull("...web.resourcehandler does not appear to be installed?!", m_resourceHandler);
  152. // Wait until the resources are all registered...
  153. waitUntilResourcesAreAvailable();
  154. // Listen to all bundle events...
  155. m_context.addBundleListener(this);
  156. }
  157. /**
  158. * Tear down for each individual test.
  159. */
  160. @Override
  161. protected void tearDown() throws Exception {
  162. try {
  163. // Delete any provisioned configuration...
  164. configureService(RESOURCE_HANDLER_PID, null);
  165. } catch (Exception exception) {
  166. // Ignore for now...
  167. exception.printStackTrace();
  168. }
  169. // Stop listening to bundle events...
  170. m_context.removeBundleListener(this);
  171. // Make sure we don't reuse old latches...
  172. m_latch = null;
  173. }
  174. protected void configureService(final String pid, Properties props) throws Exception {
  175. ServiceTracker tracker = new ServiceTracker(m_context, ConfigurationAdmin.class.getName(), null);
  176. tracker.open();
  177. ServiceRegistration reg = null;
  178. try {
  179. ConfigurationAdmin configAdmin = (ConfigurationAdmin) tracker.waitForService(TimeUnit.SECONDS.toMillis(5));
  180. assertNotNull("No configuration admin service found?!", configAdmin);
  181. final CountDownLatch latch = new CountDownLatch(1);
  182. final int configEvent = (props != null) ? ConfigurationEvent.CM_UPDATED : ConfigurationEvent.CM_DELETED;
  183. Configuration config = configAdmin.getConfiguration(pid, null);
  184. reg = m_context.registerService(ConfigurationListener.class.getName(), new ConfigurationListener() {
  185. @Override
  186. public void configurationEvent(ConfigurationEvent event) {
  187. if (pid.equals(event.getPid()) && event.getType() == configEvent) {
  188. // NOTE: this is delivered asynchronously, so it might well be that we receive the event before the configuration is actually updated...
  189. try {
  190. TimeUnit.MILLISECONDS.sleep(50);
  191. } catch (InterruptedException exception) {
  192. Thread.currentThread().interrupt();
  193. }
  194. latch.countDown();
  195. }
  196. }
  197. }, null);
  198. if (props != null) {
  199. config.update(props);
  200. } else {
  201. config.delete();
  202. }
  203. assertTrue("Configuration not provisioned in time!", latch.await(5, TimeUnit.SECONDS));
  204. } finally {
  205. if (reg != null) {
  206. reg.unregister();
  207. }
  208. tracker.close();
  209. }
  210. }
  211. /**
  212. * @return the HTTP response code for the given URL, e.g., 200 or 404.
  213. */
  214. protected String getContents(String urlToRead) throws Exception {
  215. URL url = toURL(urlToRead);
  216. HttpURLConnection conn = null;
  217. InputStream is = null;
  218. try {
  219. conn = (HttpURLConnection) url.openConnection();
  220. int rc = conn.getResponseCode();
  221. if (rc != HTTP_OK) {
  222. return null;
  223. }
  224. is = conn.getInputStream();
  225. return slurpAsString(is);
  226. } finally {
  227. if (is != null) {
  228. is.close();
  229. }
  230. if (conn != null) {
  231. conn.disconnect();
  232. }
  233. }
  234. }
  235. /**
  236. * @return the HTTP header field, can be <code>null</code>.
  237. */
  238. protected String getHeader(String urlToRead, String headerName) throws Exception {
  239. List<String> values = getHeaderValues(urlToRead, headerName);
  240. return values.isEmpty() ? null : values.get(0);
  241. }
  242. /**
  243. * @return the HTTP header field, can be <code>null</code>.
  244. */
  245. private Map<String, List<String>> getHeaders(String urlToRead) throws Exception {
  246. URL url = toURL(urlToRead);
  247. HttpURLConnection conn = null;
  248. try {
  249. conn = (HttpURLConnection) url.openConnection();
  250. int rc = conn.getResponseCode();
  251. assertEquals(HTTP_OK, rc);
  252. return conn.getHeaderFields();
  253. } finally {
  254. if (conn != null) {
  255. conn.disconnect();
  256. }
  257. }
  258. }
  259. /**
  260. * @return the values of the HTTP header field, never <code>null</code>.
  261. */
  262. protected List<String> getHeaderValues(String urlToRead, String headerName) throws Exception {
  263. Map<String, List<String>> headers = getHeaders(urlToRead);
  264. List<String> result = headers.get(headerName);
  265. return result == null ? Collections.<String> emptyList() : result;
  266. }
  267. /**
  268. * @return the HTTP response code for the given URL, e.g., 200 or 404.
  269. */
  270. protected int getResponseCode(String urlToRead) throws Exception {
  271. URL url = toURL(urlToRead);
  272. HttpURLConnection conn = null;
  273. try {
  274. conn = (HttpURLConnection) url.openConnection();
  275. int rc = conn.getResponseCode();
  276. return rc;
  277. } finally {
  278. if (conn != null) {
  279. conn.disconnect();
  280. }
  281. }
  282. }
  283. private String slurpAsString(InputStream is) throws IOException {
  284. Scanner scanner = null;
  285. try {
  286. scanner = new Scanner(is, "UTF-8");
  287. scanner.useDelimiter("\\A");
  288. return scanner.hasNext() ? scanner.next() : null;
  289. } finally {
  290. if (scanner != null) {
  291. scanner.close();
  292. }
  293. }
  294. }
  295. /**
  296. * Starts the given bundle and waits until the framework notifies all
  297. * listeners that it is started.
  298. */
  299. protected void startBundle(Bundle bundle) throws Exception {
  300. m_latch = new BundleStateLatch(bundle, BundleEvent.STARTED);
  301. bundle.start();
  302. assertTrue("Failed to start bundle " + bundle, m_latch.await());
  303. }
  304. /**
  305. * Stops the given bundle and waits until the framework notifies all
  306. * listeners that it is stopped.
  307. */
  308. protected void stopBundle(Bundle bundle) throws Exception {
  309. m_latch = new BundleStateLatch(bundle, BundleEvent.STOPPED);
  310. bundle.stop();
  311. assertTrue("Failed to stop bundle " + bundle, m_latch.await());
  312. }
  313. private URL toURL(String url) throws MalformedURLException {
  314. if (!url.startsWith(PREFIX)) {
  315. if (!url.startsWith(SLASH)) {
  316. url = PREFIX + SLASH + url;
  317. } else {
  318. url = PREFIX + url;
  319. }
  320. }
  321. return new URL(url);
  322. }
  323. /**
  324. * Checks whether all desired resources are present at start up.
  325. */
  326. protected void waitUntilResourcesAreAvailable() throws Exception {
  327. for (int i = 0; i < 10; i++) {
  328. try {
  329. if ((getResponseCode(URL_HELLO_WORLD) == HTTP_OK) && (getResponseCode(URL_WOOT) == HTTP_OK)
  330. && (getResponseCode(URL_FAREWELL_WORLD) == HTTP_OK)
  331. && (getResponseCode(URL_FAREWELL_HELLO_WORLD) == HTTP_OK)
  332. && (getResponseCode(URL_FAREWELL_AGAIN) == HTTP_OK)) {
  333. return;
  334. }
  335. } catch (IOException ex) {
  336. // Ignore for now...
  337. }
  338. TimeUnit.MILLISECONDS.sleep(100L);
  339. }
  340. fail("Web resources were not found after reasonable timeout!");
  341. }
  342. }