Package org.apache.ace.agent.impl

Source Code of org.apache.ace.agent.impl.DependencyTrackerImpl$ServiceDependencyTracker

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.apache.ace.agent.impl;

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;

/**
* Simple service dependency tracker that tracks a number of required dependencies and provides life-cycle.
*/
public class DependencyTrackerImpl {
    /**
     * Called when an individual dependency is (no longer) satisfied.
     */
    static interface DependencyCallback {
        /**
         * Called when a dependency is updated.
         *
         * @param service
         *            the new dependency, can be <code>null</code> in case the dependency is no longer available.
         */
        void updated(Object service);
    }

    /**
     * Callback interface for reporting the state of the tracked dependencies.
     */
    static interface LifecycleCallback {
        /**
         * Called when all dependencies are satisfied.
         */
        void componentStarted(BundleContext context) throws Exception;

        /**
         * Called when one or more dependencies are no longer satisfied.
         */
        void componentStopped(BundleContext context) throws Exception;
    }

    /**
     * Represents an actual dependency on an OSGi service.
     */
    private static class ServiceDependency {
        private final DependencyTrackerImpl m_manager;
        private final DependencyCallback m_calback;
        private final ServiceTracker m_tracker;
        // the actual tracked service...
        private final AtomicReference<Object> m_serviceRef;

        public ServiceDependency(DependencyTrackerImpl manager, String filterString, DependencyCallback callback) throws Exception {
            m_manager = manager;
            m_calback = callback;

            m_tracker = new ServiceDependencyTracker(this, manager.getBundleContext(), FrameworkUtil.createFilter(filterString));
            m_serviceRef = new AtomicReference<Object>();
        }

        public Object getService() {
            return m_serviceRef.get();
        }

        public boolean isServiceAvailable() {
            return getService() != null;
        }

        public void startTracking() {
            m_tracker.open();
        }

        public void stopTracking() {
            m_tracker.close();
        }

        void changed(ServiceReference ref) {
            Object service = (ref == null) ? null : m_manager.getBundleContext().getService(ref);
            Object oldService;
            do {
                oldService = m_serviceRef.get();
            }
            while (!m_serviceRef.compareAndSet(oldService, service));

            // Check on reference(!) to determine whether the service is changed...
            if (oldService != service) {
                if (m_calback != null) {
                    m_calback.updated(service);
                }

                m_manager.update();
            }
        }
    }

    /**
     * Tracker customizer that calls AgentContextDependency#changed with the highest matching service whenever something
     * changes.
     */
    private static class ServiceDependencyTracker extends ServiceTracker {
        private final CopyOnWriteArrayList<ServiceReference> m_trackedServiceRefs;
        private final ServiceDependency m_dependency;

        public ServiceDependencyTracker(ServiceDependency dependency, BundleContext context, Filter filter) {
            super(context, filter, null);
            m_dependency = dependency;
            m_trackedServiceRefs = new CopyOnWriteArrayList<ServiceReference>();
        }

        @Override
        public Object addingService(ServiceReference reference) {
            if (m_trackedServiceRefs.addIfAbsent(reference)) {
                checkForUpdate();
            }
            return super.addingService(reference);
        }

        @Override
        public void modifiedService(ServiceReference reference, Object service) {
            checkForUpdate();
        }

        @Override
        public void removedService(ServiceReference reference, Object service) {
            if (m_trackedServiceRefs.remove(reference)) {
                checkForUpdate();
            }
        }

        private void checkForUpdate() {
            ServiceReference highestReference = null;
            for (ServiceReference reference : m_trackedServiceRefs) {
                if (highestReference == null || highestReference.compareTo(reference) < 1) {
                    highestReference = reference;
                }
            }

            m_dependency.changed(highestReference);
        }
    }

    private final BundleContext m_bundleContext;
    private final LifecycleCallback m_callback;
    private final CopyOnWriteArrayList<ServiceDependency> m_dependencies;
    private final AtomicBoolean m_tracking;
    private final AtomicBoolean m_started;

    /**
     * Creates a new {@link DependencyTrackerImpl} instance.
     *
     * @param bundleContext
     *            the bundle context;
     * @param callback
     *            the component callback.
     */
    public DependencyTrackerImpl(BundleContext bundleContext, LifecycleCallback callback) {
        m_bundleContext = bundleContext;
        m_callback = callback;

        m_dependencies = new CopyOnWriteArrayList<ServiceDependency>();
        m_tracking = new AtomicBoolean(false);
        m_started = new AtomicBoolean(false);
    }

    /**
     * Adds a dependency to track.
     *
     * @param iface
     *            the interface of the dependency to track;
     * @param extraFilter
     *            an optional filter for the tracked dependency;
     * @param callback
     *            the callback to call when the dependency comes (un)available.
     */
    public void addDependency(Class<?> iface, String extraFilter, DependencyCallback callback) throws Exception {
        if (m_tracking.get()) {
            throw new IllegalStateException("Can not add new dependency while tracking is started!");
        }

        String filter = String.format("(%s=%s)", Constants.OBJECTCLASS, iface.getName());
        if (extraFilter != null) {
            filter = String.format("(&%s%s)", filter, extraFilter);
        }

        m_dependencies.addIfAbsent(new ServiceDependency(this, filter, callback));
    }

    public BundleContext getBundleContext() {
        return m_bundleContext;
    }

    /**
     * Starts tracking all dependencies, if all dependencies are satisfied,
     * {@link LifecycleCallback#componentStarted(BundleContext)} will be called. For each satisfied dependency,
     * {@link DependencyCallback#updated(Object)} is called.
     *
     * @throws IllegalStateException
     *             in case this tracker is already started.
     */
    public void startTracking() throws Exception {
        // This method should be called once and only once...
        if (!m_tracking.compareAndSet(false, true)) {
            throw new IllegalStateException("Already started tracking!");
        }

        for (ServiceDependency dependency : m_dependencies) {
            dependency.startTracking();
        }
    }

    /**
     * Stops tracking of dependencies. For each tracked dependency, {@link DependencyCallback#updated(Object)} is called
     * with a <code>null</code> value.
     *
     * @throws IllegalStateException
     *             in case this tracker is already started.
     */
    public void stopTracking() {
        // This method should be called once and only once...
        if (!m_tracking.compareAndSet(true, false)) {
            throw new IllegalStateException("Did not start tracking yet");
        }

        for (ServiceDependency dependency : m_dependencies) {
            dependency.stopTracking();
        }
    }

    /**
     * Called for each change in the dependency set. It will call
     * {@link LifecycleCallback#componentStopped(BundleContext)} if needed, and
     * {@link LifecycleCallback#componentStarted(BundleContext)} when all dependencies are met.
     */
    public void update() {
        stopComponent();

        if (allDependenciesAvailable()) {
            startComponent();
        }
    }

    /**
     * @return <code>true</code> if all dependencies are available, <code>false</code> otherwise.
     */
    final boolean allDependenciesAvailable() {
        for (ServiceDependency dependency : m_dependencies) {
            if (!dependency.isServiceAvailable()) {
                return false;
            }
        }
        return true;
    }

    /**
     * Tries to start the component, if it is not already started.
     */
    final void startComponent() {
        // Only call our callback when we're actually starting tracking dependencies...
        if (m_started.compareAndSet(false, true)) {
            try {
                m_callback.componentStarted(m_bundleContext);
            }
            catch (Exception e) {
                // really must not happen
                e.printStackTrace();
            }
        }
    }

    /**
     * Tries to stop the component, if it is not already stopped.
     */
    final void stopComponent() {
        // Only call our callback when we're actually started tracking dependencies...
        if (m_started.compareAndSet(true, false)) {
            try {
                m_callback.componentStopped(m_bundleContext);
            }
            catch (Exception e) {
                // really must not happen
                e.printStackTrace();
            }
        }
    }
}
TOP

Related Classes of org.apache.ace.agent.impl.DependencyTrackerImpl$ServiceDependencyTracker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.