org.eclipse.gemini.blueprint.test.AbstractSynchronizedOsgiTests.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.gemini.blueprint.test.AbstractSynchronizedOsgiTests.java

Source

/******************************************************************************
 * 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.util.Enumeration;

import org.eclipse.gemini.blueprint.extender.internal.util.concurrent.Counter;
import org.eclipse.gemini.blueprint.extender.support.internal.ConfigUtils;
import org.eclipse.gemini.blueprint.util.OsgiBundleUtils;
import org.eclipse.gemini.blueprint.util.OsgiListenerUtils;
import org.eclipse.gemini.blueprint.util.OsgiStringUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.springframework.util.ObjectUtils;

/**
 * JUnit superclass which offers synchronization for application context
 * initialization. The class <b>automatically</b> determines
 * <em>Spring powered</em> bundles that are installed by the testing framework
 * and (by default), will wait for their application context to fully start.
 * Only after all the application contexts have been fully refreshed, the actual
 * test execution will commence.
 * 
 * <p/>The class also provides utility waiting methods for discovering Spring
 * application context (published as OSGi services) in case programmatic waiting
 * is required (for example, when installing bundles manually).
 * 
 * <p/>As the rest of the other classes, the behaviour of this class can be
 * customized by extending its methods.
 * 
 * @author Costin Leau
 * @author Adrian Colyer
 * 
 */
public abstract class AbstractSynchronizedOsgiTests extends AbstractConfigurableOsgiTests {

    protected static final long DEFAULT_WAIT_TIME = 60L;

    private static final long SECOND = 1000;

    /**
     * 
     * Default constructor. Constructs a new
     * <code>AbstractSynchronizedOsgiTests</code> instance.
     * 
     */
    public AbstractSynchronizedOsgiTests() {
        super();
    }

    /**
     * Constructs a new <code>AbstractSynchronizedOsgiTests</code> instance.
     * 
     * @param name test name
     */
    public AbstractSynchronizedOsgiTests(String name) {
        super(name);
    }

    /**
     * Waits for a <em>Spring powered</em> bundle, given by its symbolic name
     * to be fully started.
     * 
     * <p/>Forces the current (test) thread to wait for the a Spring application
     * context to be published under the given symbolic name. This method allows
     * waiting for full initialization of Spring OSGi bundles before starting
     * the actual test execution. This method will use the test bundle context
     * for service lookup.
     * 
     * @param forBundleWithSymbolicName bundle symbolic name
     * @param timeout maximum time to wait (in seconds) for the application
     * context to be published
     */
    protected void waitOnContextCreation(String forBundleWithSymbolicName, long timeout) {
        waitOnContextCreation(bundleContext, forBundleWithSymbolicName, timeout);

    }

    /**
     * Waits for a <em>Spring powered</em> bundle, given by its symbolic name,
     * to be fully started.
     * 
     * <p/>Forces the current (test) thread to wait for the a Spring application
     * context to be published under the given symbolic name. This method allows
     * waiting for full initialization of Spring OSGi bundles before starting
     * the actual test execution.
     * 
     * @param context bundle context to use for service lookup
     * @param forBundleWithSymbolicName bundle symbolic name
     * @param timeout maximum time to wait (in seconds) for the application
     * context to be published
     */
    protected void waitOnContextCreation(BundleContext context, String forBundleWithSymbolicName, long timeout) {
        // translate from seconds to milliseconds
        long time = timeout * SECOND;

        // use the counter to make sure the threads block
        final Counter counter = new Counter("waitForContext on bnd=" + forBundleWithSymbolicName);

        counter.increment();

        String filter = "(org.springframework.context.service.name=" + forBundleWithSymbolicName + ")";

        ServiceListener listener = new ServiceListener() {

            public void serviceChanged(ServiceEvent event) {
                if (event.getType() == ServiceEvent.REGISTERED)
                    counter.decrement();
            }
        };

        OsgiListenerUtils.addServiceListener(context, listener, filter);

        if (logger.isDebugEnabled())
            logger.debug("Start waiting for Spring/OSGi bundle=" + forBundleWithSymbolicName);

        try {
            if (counter.waitForZero(time)) {
                waitingFailed(forBundleWithSymbolicName);
            } else if (logger.isDebugEnabled()) {
                logger.debug("Found applicationContext for bundle=" + forBundleWithSymbolicName);
            }
        } finally {
            // inform waiting thread
            context.removeServiceListener(listener);
        }
    }

    /**
     * Waits for a <em>Spring powered</em> bundle, given by its symbolic name,
     * to be fully started.
     * 
     * <p/>This method uses the default wait time and test bundle context and is
     * identical to #waitOnContextCreation(bundleContext,
     * forBundleWithSymbolicName, {@link #getDefaultWaitTime()}).
     * 
     * <p/>This method is used by the testing framework at startup before
     * executing the actual tests.
     * 
     * @param forBundleWithSymbolicName bundle symbolic name
     * @see #getDefaultWaitTime()
     * @see #waitOnContextCreation(BundleContext, String, long)
     */
    protected void waitOnContextCreation(String forBundleWithSymbolicName) {
        waitOnContextCreation(forBundleWithSymbolicName, getDefaultWaitTime());
    }

    private void waitingFailed(String bundleName) {
        logger.warn("Waiting for applicationContext for bundle=" + bundleName + " timed out");

        throw new RuntimeException(
                "Gave up waiting for application context for '" + bundleName + "' to be created");
    }

    /**
     * Returns the test default waiting time (in seconds). Subclasses should
     * override this method if the {@link #DEFAULT_WAIT_TIME} is not enough. For
     * more customization, consider setting
     * {@link #shouldWaitForSpringBundlesContextCreation()} to false and using
     * {@link #waitOnContextCreation(BundleContext, String, long)}.
     * 
     * @return the default wait time (in seconds) for each spring bundle context
     * to be published as an OSGi service
     */
    protected long getDefaultWaitTime() {
        return DEFAULT_WAIT_TIME;
    }

    /**
     * Indicates whether the test class should wait or not for the context
     * creation of Spring/OSGi bundles before executing the tests. Default is
     * true.
     * 
     * @return true (the default) if the test will wait for spring bundle
     * context creation or false otherwise
     */
    protected boolean shouldWaitForSpringBundlesContextCreation() {
        return true;
    }

    /*
     * Takes care of automatically waiting for the application context creation
     * of <em>Spring powered</em> bundles.
     */
    protected void postProcessBundleContext(BundleContext platformBundleContext) throws Exception {
        if (shouldWaitForSpringBundlesContextCreation()) {
            boolean debug = logger.isDebugEnabled();
            boolean trace = logger.isTraceEnabled();
            if (debug)
                logger.debug("Looking for Spring/OSGi powered bundles to wait for...");

            // determine Spring/OSGi bundles
            Bundle[] bundles = platformBundleContext.getBundles();
            for (int i = 0; i < bundles.length; i++) {
                Bundle bundle = bundles[i];
                String bundleName = OsgiStringUtils.nullSafeSymbolicName(bundle);
                if (OsgiBundleUtils.isBundleActive(bundle)) {
                    if (isSpringDMManaged(bundle) && ConfigUtils.getPublishContext(bundle.getHeaders())) {
                        if (debug)
                            logger.debug("Bundle [" + bundleName + "] triggers a context creation; waiting for it");
                        // use platformBundleContext
                        waitOnContextCreation(platformBundleContext, bundleName, getDefaultWaitTime());
                    } else if (trace)
                        logger.trace("Bundle [" + bundleName + "] does not trigger a context creation.");
                } else {
                    if (trace)
                        logger.trace("Bundle [" + bundleName + "] is not active (probably a fragment); ignoring");
                }
            }
        }
    }

    /**
     * Determines if the given bundle, is Spring DM managed or not. This method
     * is used at startup, for waiting on all Spring DM contexts to be properly
     * started and published.
     * 
     * @param bundle
     * @return
     */
    protected boolean isSpringDMManaged(Bundle bundle) {
        if (!ObjectUtils.isEmpty(ConfigUtils.getHeaderLocations(bundle.getHeaders())))
            return true;
        Enumeration enm = bundle.findEntries("META-INF/spring", "*.xml", false);
        return (enm != null && enm.hasMoreElements());
    }
}