/****************************************************************************** * 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.util.Arrays; import org.osgi.framework.Bundle; import org.osgi.framework.ServiceReference; /** * Comparator based dependency sorter. * * @author Costin Leau * @author Andy Piper */ public class ComparatorServiceDependencySorter implements ServiceDependencySorter { public Bundle[] computeServiceDependencyGraph(Bundle[] bundles) { TarganStronglyConnectedSorter parser = new TarganStronglyConnectedSorter(bundles); return parser.computeServiceDependencyGraph(); } /** * Strongly Connected Component graph algorithm due to R. E. Targan, 1972. The implementation * is adapted from "Algorithms in C" by Robert Sedgewick. In particular we make use of the property that * non-connected components are traversed depth-first and so provide us with a regular dependency graph. * Strongly connected components (a.k.a cycles) are gathered in a batch which gives us an opportunity to do * futher sorting before output. */ public static class TarganStronglyConnectedSorter { private BundleDependencyComparator comparator = new BundleDependencyComparator(); // Note that variable names reflect those in Sedgewick for easy comparison. private int id = 0; private int[] val; // visited list private int[] stack; private int p = 0; // current stack position private Node[] adj; // adjacency list of edges private int V; // the number of Vertices private Bundle[] bundles; // the output bundle list private Bundle[] sourcebundles; // the input bundle list private int index = 0; // current position in output list private static class Node { private int v; private Node next; public Node(int v, Node next) { this.v = v; this.next = next; } public int v() { return v; } public Node next() { return next; } } public TarganStronglyConnectedSorter(Bundle[] bundles) { this.V = bundles.length; this.bundles = new Bundle[V]; this.sourcebundles = bundles; val = new int[V + 1]; adj = new Node[V + 1]; stack = new int[V + 1]; } public Bundle[] computeServiceDependencyGraph() { // Zero adjacency matrix for (int k = 1; k <= V; k++) { val[k] = 0; adj[k] = null; } // Build adjacency matrix, x -> y // This probably could be more efficient for (int y = 1; y <= V; y++) { for (int x = 1; x <= V; x++) { if (references(sourcebundles[x - 1], sourcebundles[y - 1])) { adj[y] = new Node(x, adj[y]); } } } // Modified depth-first search of the nodes for (int k = 1; k <= V; k++) { if (val[k] == 0) visit(k); } return bundles; } private int visit(int k) { int m, min; val[k] = ++id; min = id; stack[p++] = k; for (Node t = adj[k]; t != null; t = t.next()) { m = (val[t.v()] == 0) ? visit(t.v()) : val[t.v()]; if (m < min) min = m; } if (min == val[k]) { int subset = index; while (stack[p] != k) { int visited = stack[--p]; bundles[index++] = sourcebundles[visited - 1]; val[visited] = V + 1; } // Strongly connected set, so sort via ranking if (index > subset) { Arrays.sort(bundles, subset, index, comparator); } } return min; } /** * Answer whether Bundle b is directly referenced by Bundle a */ protected static boolean references(Bundle a, Bundle b) { ServiceReference[] services = b.getRegisteredServices(); if (services == null) { return false; } for (int i = 0; i < services.length; i++) { // filter on spring managed services if (BundleDependencyComparator.isSpringManagedService(services[i])) { Bundle[] referingBundles = services[i].getUsingBundles(); if (referingBundles != null) { for (int j = 0; j < referingBundles.length; j++) { if (a.equals(referingBundles[j])) { return true; } } } } } return false; } } }