org.eclipse.gemini.blueprint.extender.internal.dependencies.shutdown.BundleDependencyComparator.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.gemini.blueprint.extender.internal.dependencies.shutdown.BundleDependencyComparator.java

Source

/******************************************************************************
 * Copyright (c) 2006, 2010 VMware Inc., Oracle 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.
 *   Oracle Inc.
 *****************************************************************************/

package org.eclipse.gemini.blueprint.extender.internal.dependencies.shutdown;

import java.io.Serializable;
import java.util.Comparator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;
import org.eclipse.gemini.blueprint.context.ConfigurableOsgiBundleApplicationContext;
import org.eclipse.gemini.blueprint.service.exporter.OsgiServicePropertiesResolver;
import org.eclipse.gemini.blueprint.util.OsgiServiceReferenceUtils;
import org.springframework.util.ObjectUtils;

/**
 * Null safe service-based dependency sorter for bundles. Sorts bundles based on their services using the following
 * algorithm: <p/> <ol> <li> if two bundles are connected (transitively) then the bundle that exports the service with
 * lowest ranking id, will be considered lower. </li> <li> if the ranks are equal, then the service id (which is
 * guaranteed to be unique) will be considered. </li> <li> if the bundles are not related then they will be sorted based
 * on their symbolic name. </li> </ol>
 * 
 * @author Hal Hildebrand
 * @author Andy Piper
 * @author Costin Leau
 */
public class BundleDependencyComparator implements Comparator, Serializable {

    private static final long serialVersionUID = -108354908478230663L;

    private static final Log log = LogFactory.getLog(BundleDependencyComparator.class);

    /**
     * Simple method checking whether the given service reference points to a spring managed service or not. Checks for
     * 
     * @param reference reference to the OSGi service
     * @return true if the service is spring managed, false otherwise
     */
    public static boolean isSpringManagedService(ServiceReference reference) {
        if (reference == null)
            return false;
        return (reference.getProperty(OsgiServicePropertiesResolver.BEAN_NAME_PROPERTY_KEY) != null
                || reference.getProperty(OsgiServicePropertiesResolver.SPRING_DM_BEAN_NAME_PROPERTY_KEY) != null
                || reference.getProperty(
                        ConfigurableOsgiBundleApplicationContext.APPLICATION_CONTEXT_SERVICE_PROPERTY_NAME) != null
                || reference.getProperty(
                        ConfigurableOsgiBundleApplicationContext.SPRING_DM_APPLICATION_CONTEXT_SERVICE_PROPERTY_NAME) != null);
    }

    private static ServiceReference[] excludeNonSpringManagedServices(ServiceReference[] references) {
        if (ObjectUtils.isEmpty(references))
            return references;

        int count = 0;
        for (int i = 0; i < references.length; i++) {
            if (!isSpringManagedService(references[i]))
                references[i] = null;
            else
                count++;
        }

        if (count == references.length)
            return references;

        ServiceReference[] refs = new ServiceReference[count];
        int j = 0;
        for (int i = 0; i < references.length; i++) {
            if (references[i] != null) {
                refs[j] = references[i];
                j++;
            }
        }

        return refs;
    }

    /**
     * Answer whether Bundle a is higher or lower depending on the ranking and id of its exported services. This is used
     * as a tie-breaker for circular references.
     */
    protected static int compareUsingServiceRankingAndId(Bundle a, Bundle b) {
        ServiceReference[] aservices = excludeNonSpringManagedServices(a.getRegisteredServices());
        ServiceReference[] bservices = excludeNonSpringManagedServices(b.getRegisteredServices());

        boolean trace = log.isTraceEnabled();

        // this case should not occur
        if (ObjectUtils.isEmpty(aservices) && ObjectUtils.isEmpty(bservices)) {
            if (trace)
                log.trace("both services have 0 services; sorting based on id");
            return signum((int) (a.getBundleId() - b.getBundleId()));
        } else if (aservices == null) {
            return -1;
        } else if (bservices == null) {
            return 1;
        }

        // Look for the *lowest* highest ranked service in each bundle
        // i.e. take a look at each bundle, find the highest ranking service
        // and compare it to the other bundle
        // this means that the service with the highest ranking service will
        // be shutdown last

        int aRank = findHighestServiceRanking(aservices);
        int bRank = findHighestServiceRanking(bservices);

        // since we are looking for the minimum, invert the substraction
        // (the higher bundle is the one with the lowest rank)
        if (aRank != bRank) {
            int compare = -(bRank - aRank);
            if (trace) {
                int min = (compare > 0 ? (int) bRank : (int) aRank);
                log.trace("sorting based on lowest-service-ranking won by bundle" + (compare > 0 ? "1" : "2")
                        + " w/ service id " + min);
            }

            return signum(-(bRank - aRank));
        }

        // Look for the highest id in each bundle (i.e. started last).
        long aMaxId = findHighestServiceId(aservices);
        long bMaxId = findHighestServiceId(bservices);

        if (aMaxId != bMaxId) {
            int compare = (int) (bMaxId - aMaxId);
            if (trace) {
                int max = (compare > 0 ? (int) bMaxId : (int) aMaxId);
                log.trace("sorting based on highest-service-id won by bundle " + (compare > 0 ? "1" : "2")
                        + " w/ service id " + max);
            }

            return signum(compare);
        }

        return signum((int) (a.getBundleId() - b.getBundleId()));
    }

    /**
     * Find the highest service ranking. This might come as unexpected however, since a bundle can export multiple
     * services, we have to find the minimum between the maximum services in each bundle - i.e. the bundle with the
     * highest service ranking will win.
     * 
     * @param refs
     */
    private static int findHighestServiceRanking(ServiceReference[] refs) {
        int maxRank = Integer.MIN_VALUE;
        for (int i = 0; i < refs.length; i++) {
            int currentRank = OsgiServiceReferenceUtils.getServiceRanking(refs[i]);
            if (currentRank > maxRank)
                maxRank = currentRank;
        }

        return maxRank;
    }

    private static long findHighestServiceId(ServiceReference[] refs) {
        long maxId = Long.MIN_VALUE;
        for (int i = 0; i < refs.length; i++) {
            long currentId = OsgiServiceReferenceUtils.getServiceId(refs[i]);
            if (currentId > maxId)
                maxId = currentId;
        }

        return maxId;
    }

    private static int signum(int a) {
        return a < 0 ? -1 : a == 0 ? 0 : 1;
    }

    public int compare(Object a, Object b) {
        return compareUsingServiceRankingAndId((Bundle) a, (Bundle) b);
    }
}