/org.amdatu.web.resourcehandler.itest/src/org/amdatu/web/resourcehandler/itest/ResourceHandlerTestBase.java
Java | 393 lines | 272 code | 51 blank | 70 comment | 47 complexity | 3d8b4af1ea6ab28404e32de151819e74 MD5 | raw file
- /**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.amdatu.web.resourcehandler.itest;
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.HttpURLConnection;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.util.Collections;
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
- import java.util.Scanner;
- import java.util.concurrent.CountDownLatch;
- import java.util.concurrent.TimeUnit;
- import org.apache.felix.dm.tracker.ServiceTracker;
- import org.osgi.framework.Bundle;
- import org.osgi.framework.BundleContext;
- import org.osgi.framework.BundleEvent;
- import org.osgi.framework.BundleListener;
- import org.osgi.framework.FrameworkUtil;
- import org.osgi.framework.ServiceRegistration;
- import org.osgi.service.cm.Configuration;
- import org.osgi.service.cm.ConfigurationAdmin;
- import org.osgi.service.cm.ConfigurationEvent;
- import org.osgi.service.cm.ConfigurationListener;
- import junit.framework.TestCase;
- public abstract class ResourceHandlerTestBase extends TestCase implements BundleListener {
- protected static final String RESOURCE_HANDLER_PID = "org.amdatu.web.resourcehandler";
- protected static final int HTTP_OK = 200;
- protected static final int HTTP_NOT_FOUND = 404;
- private static final String PREFIX = "http://localhost:8080";
- private static final String SLASH = "/";
- protected static final String URL_HELLO_WORLD = "bundle1/helloworld.txt";
- protected static final String URL_FAREWELL_WORLD = "bundle2/farewellworld1.txt";
- protected static final String URL_FAREWELL_AGAIN = "bundle2/farewellworld2.txt";
- protected static final String URL_FAREWELL_HELLO_WORLD = "bundle1/bundle2/farewellworld.txt";
- protected static final String URL_BUNDLE4_ROOT_WITHOUT_DEFAULT = "bundle4/";
- protected static final String URL_BUNDLE4_ROOT_WITH_DEFAULT = "bundle4/default.txt";
- protected static final String URL_BUNDLE4_A_WITHOUT_SLASH = "bundle4/a";
- protected static final String URL_BUNDLE4_A_WITHOUT_DEFAULT = "bundle4/a/";
- protected static final String URL_BUNDLE4_A_WITH_DEFAULT = "bundle4/a/a.txt";
- protected static final String URL_BUNDLE4_B_WITHOUT_DEFAULT = "bundle4/b/";
- protected static final String URL_BUNDLE4_B_WITH_DEFAULT = "bundle4/b/default.txt";
- protected static final String URL_BUNDLE4_B_NON_EXISTING = "bundle4/b/nonExisting.txt";
- protected static final String URL_BUNDLE4_C_WITHOUT_SLASH = "bundle4/c";
- protected static final String URL_BUNDLE4_C_C = "bundle4/c/c.txt";
- protected static final String URL_BUNDLE4_D_WITHOUT_SLASH = "bundle4/d";
- protected static final String URL_BUNDLE4_D_NON_EXISTING = "bundle4/d/nonExisting.txt";
- protected static final String URL_BUNDLE5_ROOT_WITHOUT_DEFAULT = "bundle5/";
- protected static final String URL_BUNDLE5_ROOT_WITH_DEFAULT = "bundle5/default.txt";
- protected static final String URL_WOOT = "http://localhost:8080/bundle3/woot.txt";
- protected static final String URL_BUNDLE1_WITHOUT_SLASH = "bundle1";
- protected static final String URL_BUNDLE1_WITH_SLASH = "bundle1/";
- protected static final String URL_BUNDLE4_WITHOUT_SLASH = "bundle4";
- protected static final String URL_BUNDLE4_WITH_SLASH = "bundle4/";
- protected static final String URL_BUNDLE6_FULL_WITHOUT_SLASH = "http://localhost:8080";
- protected static final String URL_BUNDLE6_FULL_WITH_SLASH = "http://localhost:8080/";
- protected static final String URL_BUNDLE6_WITH_SLASH = "/";
- protected static final String URL_BUNDLE6_WITHOUT_SLASH = "";
- protected static final String URL_BUNDLE6_WITH_DEFAULT = "/default.txt";
- protected static final String URL_BUNDLE6_X = "/x.txt";
- protected static final String DEFAULT_CACHE_CONTROL_VALUE = "max-age=604800, must-revalidate";
- protected static final String HDR_CACHE_CONTROL = "Cache-Control";
- protected final BundleContext m_context;
- private volatile BundleStateLatch m_latch;
- protected Bundle m_helloBundle;
- private Bundle m_farewellBundle;
- protected Bundle m_resourceHandler;
- protected Bundle m_bundleOwnContext;
- protected ResourceHandlerTestBase() {
- m_context = FrameworkUtil.getBundle(getClass()).getBundleContext();
- }
- /**
- * Provides a custom {@link CountDownLatch} that allows it to count down in
- * case a bundle event for a specified bundle with a specified bundle
- * event-type is given.
- */
- protected static class BundleStateLatch {
- private final CountDownLatch m_latch;
- private final Bundle m_bundle;
- private final int m_type;
- public BundleStateLatch(Bundle bundle, int type) {
- m_latch = new CountDownLatch(1);
- m_bundle = bundle;
- m_type = type;
- }
- /**
- * Awaits until this latch reaches a count of zero or when 5 seconds
- * are passed, whichever comes first.
- */
- public boolean await() throws InterruptedException {
- return m_latch.await(5, TimeUnit.SECONDS);
- }
- /**
- * Counts down in case the given bundle event matches the contained
- * bundle and type.
- *
- * @param event the bundle event to test, cannot be <code>null</code>.
- * @return <code>true</code> if the event matched, <code>false</code> otherwise.
- */
- public boolean countDownOnMatch(BundleEvent event) {
- if (event.getBundle() == m_bundle && event.getType() == m_type) {
- m_latch.countDown();
- return true;
- }
- return false;
- }
- }
- public void bundleChanged(BundleEvent event) {
- if (m_latch != null) {
- m_latch.countDownOnMatch(event);
- }
- }
- /**
- * Set up for each individual test.
- */
- @Override
- protected void setUp() throws Exception {
- Bundle[] bundles = m_context.getBundles();
- for (Bundle bundle : bundles) {
- String symbolicName = bundle.getSymbolicName();
- if (RESOURCE_HANDLER_PID.equals(symbolicName)) {
- m_resourceHandler = bundle;
- }
- if ("org.amdatu.web.resourcehandler.itest.bundle1".equals(symbolicName)) {
- m_helloBundle = bundle;
- }
- if ("org.amdatu.web.resourcehandler.itest.bundle2".equals(symbolicName)) {
- m_farewellBundle = bundle;
- }
- if ("org.amdatu.web.resourcehandler.itest.bundle3".equals(symbolicName)) {
- m_bundleOwnContext = bundle;
- }
- }
- // Sanity check before entering any test...
- assertNotNull("...itest.bundle1 does not appear to be installed?!", m_helloBundle);
- assertNotNull("...itest.bundle2 does not appear to be installed?!", m_farewellBundle);
- assertNotNull("...itest.bundle3 does not appear to be installed?!", m_bundleOwnContext);
- assertNotNull("...web.resourcehandler does not appear to be installed?!", m_resourceHandler);
- // Wait until the resources are all registered...
- waitUntilResourcesAreAvailable();
- // Listen to all bundle events...
- m_context.addBundleListener(this);
- }
- /**
- * Tear down for each individual test.
- */
- @Override
- protected void tearDown() throws Exception {
- try {
- // Delete any provisioned configuration...
- configureService(RESOURCE_HANDLER_PID, null);
- } catch (Exception exception) {
- // Ignore for now...
- exception.printStackTrace();
- }
- // Stop listening to bundle events...
- m_context.removeBundleListener(this);
- // Make sure we don't reuse old latches...
- m_latch = null;
- }
- protected void configureService(final String pid, Properties props) throws Exception {
- ServiceTracker tracker = new ServiceTracker(m_context, ConfigurationAdmin.class.getName(), null);
- tracker.open();
- ServiceRegistration reg = null;
- try {
- ConfigurationAdmin configAdmin = (ConfigurationAdmin) tracker.waitForService(TimeUnit.SECONDS.toMillis(5));
- assertNotNull("No configuration admin service found?!", configAdmin);
- final CountDownLatch latch = new CountDownLatch(1);
- final int configEvent = (props != null) ? ConfigurationEvent.CM_UPDATED : ConfigurationEvent.CM_DELETED;
- Configuration config = configAdmin.getConfiguration(pid, null);
- reg = m_context.registerService(ConfigurationListener.class.getName(), new ConfigurationListener() {
- @Override
- public void configurationEvent(ConfigurationEvent event) {
- if (pid.equals(event.getPid()) && event.getType() == configEvent) {
- // NOTE: this is delivered asynchronously, so it might well be that we receive the event before the configuration is actually updated...
- try {
- TimeUnit.MILLISECONDS.sleep(50);
- } catch (InterruptedException exception) {
- Thread.currentThread().interrupt();
- }
- latch.countDown();
- }
- }
- }, null);
- if (props != null) {
- config.update(props);
- } else {
- config.delete();
- }
- assertTrue("Configuration not provisioned in time!", latch.await(5, TimeUnit.SECONDS));
- } finally {
- if (reg != null) {
- reg.unregister();
- }
- tracker.close();
- }
- }
- /**
- * @return the HTTP response code for the given URL, e.g., 200 or 404.
- */
- protected String getContents(String urlToRead) throws Exception {
- URL url = toURL(urlToRead);
- HttpURLConnection conn = null;
- InputStream is = null;
- try {
- conn = (HttpURLConnection) url.openConnection();
- int rc = conn.getResponseCode();
- if (rc != HTTP_OK) {
- return null;
- }
- is = conn.getInputStream();
- return slurpAsString(is);
- } finally {
- if (is != null) {
- is.close();
- }
- if (conn != null) {
- conn.disconnect();
- }
- }
- }
- /**
- * @return the HTTP header field, can be <code>null</code>.
- */
- protected String getHeader(String urlToRead, String headerName) throws Exception {
- List<String> values = getHeaderValues(urlToRead, headerName);
- return values.isEmpty() ? null : values.get(0);
- }
- /**
- * @return the HTTP header field, can be <code>null</code>.
- */
- private Map<String, List<String>> getHeaders(String urlToRead) throws Exception {
- URL url = toURL(urlToRead);
- HttpURLConnection conn = null;
- try {
- conn = (HttpURLConnection) url.openConnection();
- int rc = conn.getResponseCode();
- assertEquals(HTTP_OK, rc);
- return conn.getHeaderFields();
- } finally {
- if (conn != null) {
- conn.disconnect();
- }
- }
- }
- /**
- * @return the values of the HTTP header field, never <code>null</code>.
- */
- protected List<String> getHeaderValues(String urlToRead, String headerName) throws Exception {
- Map<String, List<String>> headers = getHeaders(urlToRead);
- List<String> result = headers.get(headerName);
- return result == null ? Collections.<String> emptyList() : result;
- }
- /**
- * @return the HTTP response code for the given URL, e.g., 200 or 404.
- */
- protected int getResponseCode(String urlToRead) throws Exception {
- URL url = toURL(urlToRead);
- HttpURLConnection conn = null;
- try {
- conn = (HttpURLConnection) url.openConnection();
- int rc = conn.getResponseCode();
- return rc;
- } finally {
- if (conn != null) {
- conn.disconnect();
- }
- }
- }
- private String slurpAsString(InputStream is) throws IOException {
- Scanner scanner = null;
- try {
- scanner = new Scanner(is, "UTF-8");
- scanner.useDelimiter("\\A");
- return scanner.hasNext() ? scanner.next() : null;
- } finally {
- if (scanner != null) {
- scanner.close();
- }
- }
- }
- /**
- * Starts the given bundle and waits until the framework notifies all
- * listeners that it is started.
- */
- protected void startBundle(Bundle bundle) throws Exception {
- m_latch = new BundleStateLatch(bundle, BundleEvent.STARTED);
- bundle.start();
- assertTrue("Failed to start bundle " + bundle, m_latch.await());
- }
- /**
- * Stops the given bundle and waits until the framework notifies all
- * listeners that it is stopped.
- */
- protected void stopBundle(Bundle bundle) throws Exception {
- m_latch = new BundleStateLatch(bundle, BundleEvent.STOPPED);
- bundle.stop();
- assertTrue("Failed to stop bundle " + bundle, m_latch.await());
- }
- private URL toURL(String url) throws MalformedURLException {
- if (!url.startsWith(PREFIX)) {
- if (!url.startsWith(SLASH)) {
- url = PREFIX + SLASH + url;
- } else {
- url = PREFIX + url;
- }
- }
- return new URL(url);
- }
- /**
- * Checks whether all desired resources are present at start up.
- */
- protected void waitUntilResourcesAreAvailable() throws Exception {
- for (int i = 0; i < 10; i++) {
- try {
- if ((getResponseCode(URL_HELLO_WORLD) == HTTP_OK) && (getResponseCode(URL_WOOT) == HTTP_OK)
- && (getResponseCode(URL_FAREWELL_WORLD) == HTTP_OK)
- && (getResponseCode(URL_FAREWELL_HELLO_WORLD) == HTTP_OK)
- && (getResponseCode(URL_FAREWELL_AGAIN) == HTTP_OK)) {
- return;
- }
- } catch (IOException ex) {
- // Ignore for now...
- }
- TimeUnit.MILLISECONDS.sleep(100L);
- }
- fail("Web resources were not found after reasonable timeout!");
- }
- }