All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.jguru.nazgul.core.osgi.launcher.api.AbstractFrameworkLauncher Maven / Gradle / Ivy

/*
 * #%L
 * Nazgul Project: nazgul-core-osgi-launcher-api
 * %%
 * Copyright (C) 2010 - 2013 jGuru Europe AB
 * %%
 * Licensed under the jGuru Europe AB license (the "License"), based
 * on Apache License, Version 2.0; you may not use this file except
 * in compliance with the License.
 * 
 * You may obtain a copy of the License at
 * 
 *       http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt
 * 
 * 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.
 * #L%
 */

package se.jguru.nazgul.core.osgi.launcher.api;

import org.apache.commons.lang3.Validate;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.launch.Framework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.jguru.nazgul.core.algorithms.event.api.producer.AbstractEventProducer;
import se.jguru.nazgul.core.clustering.api.ConstantIdGenerator;
import se.jguru.nazgul.core.clustering.api.IdGenerator;
import se.jguru.nazgul.core.osgi.launcher.api.event.BundleContextHolder;
import se.jguru.nazgul.core.osgi.launcher.api.event.OsgiFrameworkListener;

import java.util.HashMap;
import java.util.Map;

/**
 * Abstract implementation of the FrameworkLauncher interface, which caters
 * for registering and deregistering EventConsumers (in the form of
 * OsgiFrameworkListener instances) to OSGi ServiceEvents.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public abstract class AbstractFrameworkLauncher extends AbstractEventProducer
        implements FrameworkLauncher {

    // Our log
    private static final Logger log = LoggerFactory.getLogger(AbstractFrameworkLauncher.class);

    // Internal state
    /**
     * The OSGi Framework reference.
     */
    protected Framework framework;
    private final Object[] lock = new Object[0];
    private Map toRegister = new HashMap();
    private Map toDeregister = new HashMap();

    /**
     * Creates a new AbstractFrameworkLauncher with the provided IdGenerator.
     *
     * @param idGenerator The ID generator used to acquire a cluster-unique identifier for
     *                    this AbstractFrameworkLauncher instance.
     */
    protected AbstractFrameworkLauncher(final IdGenerator idGenerator, final Class tClass) {
        super(idGenerator, tClass);
    }

    /**
     * Creates a new AbstractFrameworkLauncher and assigns the provided
     * cluster-unique ID to this instance.
     *
     * @param clusterUniqueID A cluster-unique Identifier.
     */
    protected AbstractFrameworkLauncher(final String clusterUniqueID, final Class tClass) {
        this(new ConstantIdGenerator(clusterUniqueID), tClass);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void initialize(final Map configuration) throws IllegalStateException {

        // Check sanity
        Validate.notNull(configuration, "Cannot handle null configuration Map.");

        // Create the Framework, and register ourselves as a FrameworkListener
        framework = createFramework(configuration);

        try {

            // Initialize the created OSGi Framework.
            framework.init();

            // Perform any custom processing.
            onInitialize(configuration, framework);

        } catch (final BundleException e) {
            throw new IllegalStateException("Could not initialize the OSGi Framework", e);
        }
    }

    /**
     * Creates the OSGi Framework instance from the data within the provided configuration.
     *
     * @param configuration The configuration properties given at initialization time to this ContainerEmbedder.
     * @return The initialized - but not started - OSGi Framework reference.
     * @throws IllegalArgumentException if the configuration was deemed inappropriate for
     *                                  initializing the underlying container.
     */
    protected abstract Framework createFramework(final Map configuration)
            throws IllegalArgumentException;

    /**
     * {@inheritDoc} Delegates processing to the {@code onStart} method.
     */
    @Override
    public final void start() throws BundleException, IllegalStateException {

        if (framework == null) {
            throw new IllegalStateException("OSGi Framework not created. Did you initialize it?");
        }

        // Start the osgiFramework.
        framework.start();

        // Synchronize listeners
        synchronizeListenerRegistration();

        // Cater for custom event implementation.
        onStart(framework);
    }

    /**
     * Custom handler for the init event - override this method
     * if you need to implement some custom logic to execute after
     * the Framework is initialized.
     *
     * @param framework     The just initialized osgiFramework instance.
     * @param configuration A Map holding configuration data for the underlying OSGi container.
     */
    protected void onInitialize(final Map configuration, final Framework framework) {
        // Default implementation does nothing.
    }

    /**
     * Custom handler for the start event - override this method
     * if you need to implement some custom logic when the Framework
     * is started.
     *
     * @param framework The just started osgiFramework instance.
     */
    protected void onStart(final Framework framework) {
        // Default implementation does nothing.
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Framework getFramework() {
        return framework;
    }

    /**
     * {@inheritDoc} Delegates processing to the {@code onStop} method.
     */
    @Override
    public final void stop() throws BundleException, IllegalStateException {

        if (framework == null) {
            throw new IllegalStateException("OSGi Framework not created. Did you initialize it?");
        }

        // Stop the framework
        framework.stop();

        // Synchronize listeners
        synchronizeListenerRegistration();

        // Cater for custom event implementation.
        onStop(framework);
    }

    /**
     * Custom handler for the stop event - override this method
     * if you need to implement some custom logic after the Framework
     * is stopped.
     *
     * @param framework The just stopped OSGi Framework.
     */
    protected void onStop(final Framework framework) {
        // Default implementation does nothing.
    }

    /**
     * {@inheritDoc}
     */
    public void synchronizeListenerRegistration() {

        deregisterQueuedListeners();
        registerQueuedListeners();
    }

    /**
     * Internal factory method creating a BundleContextHolder instance from
     * the supplied validEventConsumer.
     *
     * @param validEventConsumer A valid EventConsumer instance.
     * @return A BundleContextHolder wrapping the provided validEventConsumer.
     */
    protected abstract BundleContextHolder makeBundleContextHolder(final T validEventConsumer);

    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean registerConsumerToEventProducers(final T validEventConsumer) {

        final BundleContextHolder bundleContextHolder = makeBundleContextHolder(validEventConsumer);

        if (getFramework() == null || getFramework().getBundleContext() == null) {

            // Defer registration until startup
            toRegister.put(bundleContextHolder.getClusterId(), bundleContextHolder);

        } else {

            final BundleContext bundleContext = getFramework().getBundleContext();

            try {
                bundleContextHolder.register(bundleContext);
            } catch (Exception e) {

                log.error("Could not add [" + bundleContextHolder.getClusterId() + "] as an EventConsumer", e);
                bundleContextHolder.unregister(bundleContext);
                return false;
            }
        }

        // All done.
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean removeConsumerFromEventProducers(final T validEventConsumer) {

        final BundleContextHolder bundleContextHolder = makeBundleContextHolder(validEventConsumer);

        if (getFramework() == null || getFramework().getBundleContext() == null) {

            // Defer deregistration until shutdown
            toDeregister.put(bundleContextHolder.getClusterId(), bundleContextHolder);

        } else {

            final BundleContext bundleContext = getFramework().getBundleContext();

            try {
                bundleContextHolder.unregister(bundleContext);

            } catch (Exception e) {

                log.error("Could not remove [" + bundleContextHolder.getClusterId() + "] as an EventConsumer", e);
                return false;
            }

        }

        // All done.
        return true;
    }

    //
    // Private helpers
    //

    private void registerQueuedListeners() {

        synchronized (lock) {

            // Alter the registration list
            final Map containerEventListeners = toRegister;
            if (getFramework() == null) {

                log.warn("Abandoning [registerQueuedListeners]: OSGi Framework is null. (Check your lifecycle).");
                return;
            }

            final BundleContext ctx = getFramework().getBundleContext();
            if (ctx != null) {

                // All seems OK. Proceed with registering.
                this.toRegister = new HashMap();

                for (Map.Entry current : containerEventListeners.entrySet()) {

                    // Register the ContainerEventListener
                    BundleContextHolder bundleContextHolder = current.getValue();

                    try {
                        bundleContextHolder.register(ctx);
                    } catch (Exception e) {
                        log.error("Could not add [" + bundleContextHolder.getClusterId()
                                + "] as a ServiceListener. Abandoning.", e);

                        // Rollback this particular listener.
                        bundleContextHolder.unregister(ctx);
                    }
                }

                // Remove the old elements from the list, to invoke GC.
                containerEventListeners.clear();
            } else {
                throw new IllegalStateException(
                        "Could not acquire BundleContext from framework. This can happen if you forget to start "
                                + "the framework before using it. You might also need to check the installation.");
            }
        }
    }

    private void deregisterQueuedListeners() {

        synchronized (lock) {

            // Alter the registration list
            final Map containerEventListeners = toDeregister;
            if (getFramework() == null) {

                log.warn("Abandoning [deregisterQueuedListeners]: OSGi Framework is null. (Check your lifecycle).");
                return;
            }

            final BundleContext ctx = getFramework().getBundleContext();
            if (ctx != null) {

                // All seems OK. Proceed with deregistering.
                this.toDeregister = new HashMap();

                for (Map.Entry current : containerEventListeners.entrySet()) {

                    final BundleContextHolder wrapper = current.getValue();

                    try {
                        wrapper.unregister(ctx);
                    } catch (Exception e) {

                        log.error("Could not deregister [" + wrapper.getClusterId() + "]. Listener state may be corrupt.", e);
                        this.toDeregister.put(wrapper.getClusterId(), wrapper);
                    }
                }

                // Remove all listeners from the old queue list.
                containerEventListeners.clear();
            } else {
                throw new IllegalStateException(
                        "Could not acquire BundleContext from framework. This can happen if you forget to start "
                                + "the framework before using it. You might also need to check the installation.");
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy