Package org.apache.geronimo.mavenplugins.osgi.utils

Source Code of org.apache.geronimo.mavenplugins.osgi.utils.BundleResolver

/*
* 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.geronimo.mavenplugins.osgi.utils;

import static org.eclipse.osgi.service.resolver.ResolverError.IMPORT_PACKAGE_USES_CONFLICT;
import static org.eclipse.osgi.service.resolver.ResolverError.MISSING_FRAGMENT_HOST;
import static org.eclipse.osgi.service.resolver.ResolverError.MISSING_IMPORT_PACKAGE;
import static org.eclipse.osgi.service.resolver.ResolverError.MISSING_REQUIRE_BUNDLE;
import static org.eclipse.osgi.service.resolver.ResolverError.REQUIRE_BUNDLE_USES_CONFLICT;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.BundleSpecification;
import org.eclipse.osgi.service.resolver.ExportPackageDescription;
import org.eclipse.osgi.service.resolver.HostSpecification;
import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
import org.eclipse.osgi.service.resolver.ResolverError;
import org.eclipse.osgi.service.resolver.State;
import org.eclipse.osgi.service.resolver.StateObjectFactory;
import org.eclipse.osgi.service.resolver.VersionConstraint;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;

public class BundleResolver {
    private static final String PROP_MAVEN_PROJECT = "MavenProject";
    private static final String PROP_MANIFEST = "BundleManifest";

    private StateObjectFactory factory = StateObjectFactory.defaultFactory;
    private State state;
    private long id = 0;
    private Logger logger;

    public static BundleDescription[] getDependentBundles(BundleDescription root) {
        if (root == null)
            return new BundleDescription[0];
        BundleDescription[] imported = getImportedBundles(root);
        BundleDescription[] required = getRequiredBundles(root);
        BundleDescription[] dependents = new BundleDescription[imported.length + required.length];
        System.arraycopy(imported, 0, dependents, 0, imported.length);
        System.arraycopy(required, 0, dependents, imported.length, required.length);
        return dependents;
    }

    public static BundleDescription[] getImportedBundles(BundleDescription root) {
        if (root == null)
            return new BundleDescription[0];
        ExportPackageDescription[] packages = root.getResolvedImports();
        List<BundleDescription> resolvedImports = new ArrayList<BundleDescription>(packages.length);
        for (int i = 0; i < packages.length; i++)
            if (!root.getLocation().equals(packages[i].getExporter().getLocation()) && !resolvedImports
                .contains(packages[i].getExporter()))
                resolvedImports.add(packages[i].getExporter());
        return (BundleDescription[])resolvedImports.toArray(new BundleDescription[resolvedImports.size()]);
    }

    public static BundleDescription[] getRequiredBundles(BundleDescription root) {
        if (root == null)
            return new BundleDescription[0];
        return root.getResolvedRequires();
    }

    public BundleResolver(Logger logger) {
        this.logger = logger;
        this.state = factory.createState(true);
        Properties props = new Properties();
        props.putAll(System.getProperties());
        BundleUtil.loadVMProfile(props);
        state.setPlatformProperties(props);
        URL url = Bundle.class.getProtectionDomain().getCodeSource().getLocation();

        File osgi = toFile(url);
        try {
            addBundle(osgi);
        } catch (BundleException e) {
            throw new IllegalArgumentException(e);
        }
    }

    private long getNextId() {
        return ++id;
    }

    public BundleDescription addBundle(File bundleLocation) throws BundleException {
        return addBundle(bundleLocation, false);
    }

    public BundleDescription addBundle(File bundleLocation, boolean override) throws BundleException {
        if (bundleLocation == null || !bundleLocation.exists())
            throw new IllegalArgumentException("bundleLocation not found: " + bundleLocation);
        Dictionary manifest = loadManifestAttributes(bundleLocation);
        if (manifest == null) {
            throw new BundleException("Manifest not found in " + bundleLocation);
        }
        return addBundle(manifest, bundleLocation, override);
    }

    public BundleDescription addBundle(File manifestLocation, File bundleLocation, boolean override)
        throws BundleException {
        if (bundleLocation == null || !bundleLocation.exists())
            throw new IllegalArgumentException("bundleLocation not found: " + bundleLocation);
        Dictionary manifest = loadManifestAttributes(manifestLocation);
        if (manifest == null)
            throw new IllegalArgumentException("Manifest not found in " + manifestLocation);
        return addBundle(manifest, bundleLocation, override);
    }

    private Dictionary loadManifestAttributes(File bundleLocation) {
        Manifest m = loadManifest(bundleLocation);
        if (m == null) {
            return null;
        }

        return manifestToProperties(m.getMainAttributes());
    }

    public Manifest loadManifest(File bundleLocation) {
        try {
            return BundleUtil.getManifest(bundleLocation);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private Properties manifestToProperties(Attributes d) {
        Iterator iter = d.keySet().iterator();
        Properties result = new Properties();
        while (iter.hasNext()) {
            Attributes.Name key = (Attributes.Name)iter.next();
            result.put(key.toString(), d.get(key));
        }
        return result;
    }

    private BundleDescription addBundle(Dictionary enhancedManifest, File bundleLocation, boolean override)
        throws BundleException {

        BundleDescription descriptor =
            factory.createBundleDescription(state, enhancedManifest, bundleLocation.getAbsolutePath(), getNextId());

        setUserProperty(descriptor, PROP_MANIFEST, enhancedManifest);
        if (override) {
            BundleDescription[] conflicts = state.getBundles(descriptor.getSymbolicName());
            if (conflicts != null) {
                for (BundleDescription conflict : conflicts) {
                    state.removeBundle(conflict);
                    logger
                        .warn(conflict.toString() + " has been replaced by another bundle with the same symbolic name "
                            + descriptor.toString());
                }
            }
        }

        state.addBundle(descriptor);
        return descriptor;
    }

    public BundleDescription getResolvedBundle(String bundleId) {
        BundleDescription[] description = state.getBundles(bundleId);
        if (description == null)
            return null;
        for (int i = 0; i < description.length; i++) {
            if (description[i].isResolved())
                return description[i];
        }
        return null;
    }

    public void resolveState() {
        state.resolve(false);

        if (logger.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder("Resolved OSGi state\n");
            for (BundleDescription bundle : state.getBundles()) {
                if (!bundle.isResolved()) {
                    sb.append("[X] ");
                } else {
                    sb.append("[V] ");
                }
                sb.append(bundle).append(": (").append(bundle.getLocation());
                sb.append(")\n");
                for (ResolverError error : state.getResolverErrors(bundle)) {
                    sb.append("    ").append(error.toString()).append('\n');
                }
            }
            logger.debug(sb.toString());
        }
    }

    public State getState() {
        return state;
    }

    public BundleDescription[] getBundles() {
        return state.getBundles();
    }

    public ResolverError[] getResolverErrors(BundleDescription bundle) {
        Set<ResolverError> errors = new LinkedHashSet<ResolverError>();
        getRelevantErrors(errors, bundle);
        return (ResolverError[])errors.toArray(new ResolverError[errors.size()]);
    }

    private void getRelevantErrors(Set<ResolverError> errors, BundleDescription bundle) {
        ResolverError[] bundleErrors = state.getResolverErrors(bundle);
        for (int j = 0; j < bundleErrors.length; j++) {
            ResolverError error = bundleErrors[j];
            errors.add(error);

            VersionConstraint constraint = error.getUnsatisfiedConstraint();
            if (constraint instanceof BundleSpecification || constraint instanceof HostSpecification) {
                BundleDescription[] requiredBundles = state.getBundles(constraint.getName());
                for (int i = 0; i < requiredBundles.length; i++) {
                    getRelevantErrors(errors, requiredBundles[i]);
                }
            }
        }
    }

    private void logError(BundleDescription bundle, int level, Object object) {
        StringBuffer msg = new StringBuffer();
        for (int i = 0; i < level; i++) {
            msg.append("--");
        }
        msg.append("> [").append(bundle.getSymbolicName()).append("] ");
        msg.append(object);
        logger.error(msg.toString());
    }

    public void analyzeErrors(BundleDescription bundle) {
        analyzeErrors(bundle, new HashSet<BundleDescription>(), 1);
    }

    private void analyzeErrors(BundleDescription bundle, Set<BundleDescription> bundles, int level) {
        if (bundles.contains(bundle)) {
            return;
        }
        bundles.add(bundle);
        ResolverError[] errors = state.getResolverErrors(bundle);
        for (ResolverError error : errors) {
            logError(bundle, level, error);
            VersionConstraint constraint = error.getUnsatisfiedConstraint();
            switch (error.getType()) {
                case MISSING_IMPORT_PACKAGE:
                    ImportPackageSpecification pkgSpec = (ImportPackageSpecification)constraint;
                    for (BundleDescription b : getBundles()) {
                        for (ExportPackageDescription pkg : b.getExportPackages()) {
                            if (pkg.getName().equals(pkgSpec.getName())) {
                                if (pkgSpec.getVersionRange().isIncluded(pkg.getVersion())) {
                                    if (!pkg.getExporter().isResolved()) {
                                        logError(b, level, "Bundle unresolved: " + pkg);
                                        analyzeErrors(pkg.getExporter(), bundles, level + 1);
                                    }
                                } else {
                                    logError(b, level, "Version mismatch: " + pkgSpec + " " + pkg);
                                }
                            }
                        }
                    }
                    break;
                case MISSING_REQUIRE_BUNDLE:
                case MISSING_FRAGMENT_HOST:
                    // BundleSpecification bundleSpec = (BundleSpecification)constraint;
                    for (BundleDescription b : getBundles()) {
                        if (b == bundle) {
                            continue;
                        }
                        if (b.getSymbolicName() == null) {
                            logError(b, level, "No SymbolicName for " + b.getLocation());
                            continue;
                        }
                        if (constraint.getName() == null) {
                            logError(bundle, level, "no constraint name: " + constraint);
                        }
                        if (b.getSymbolicName().equals(constraint.getName())) {
                            if (constraint.getVersionRange().isIncluded(b.getVersion())) {
                                // There must be something wrong in the bundle
                                analyzeErrors(b, bundles, level);
                            } else {
                                logError(bundle, level, "Version mismatch: " + constraint + " " + b);
                            }
                        }
                    }
                    break;
                case IMPORT_PACKAGE_USES_CONFLICT:
                    ImportPackageSpecification importPackage = (ImportPackageSpecification)constraint;
                    for (BundleDescription b : getBundles()) {
                        for (ExportPackageDescription pkg : b.getExportPackages()) {
                            if (pkg.getName().equals(importPackage.getName())) {
                                logError(pkg.getExporter(), level + 1, pkg.toString());
                            }
                        }
                    }
                    break;
                case REQUIRE_BUNDLE_USES_CONFLICT:
                default:  
                    // error is already logged
                    break;
            }
        }

    }

    public Set<ResolverError> getAllErrors() {
        BundleDescription[] bundles = state.getBundles();
        Set<ResolverError> errors = new LinkedHashSet<ResolverError>();
        for (int i = 0; i < bundles.length; i++) {
            BundleDescription bundle = bundles[i];
            ResolverError[] bundleErrors = state.getResolverErrors(bundle);
            if (bundleErrors != null) {
                errors.addAll(Arrays.asList(bundleErrors));
            }
        }
        return errors;
    }

    public List<BundleDescription> getDependencies(BundleDescription desc) {
        Set<Long> bundleIds = new LinkedHashSet<Long>();
        addBundleAndDependencies(desc, bundleIds, true);
        List<BundleDescription> dependencies = new ArrayList<BundleDescription>();
        for (long bundleId : bundleIds) {
            if (desc.getBundleId() != bundleId) {
                BundleDescription dependency = state.getBundle(bundleId);
                BundleDescription supplier = dependency.getSupplier().getSupplier();
                HostSpecification host = supplier.getHost();
                if (host == null || !desc.equals(host.getSupplier())) {
                    dependencies.add(dependency);
                }
            }
        }
        return dependencies;
    }

    /**
     * Code below is copy&paste from org.eclipse.pde.internal.core.DependencyManager
     * which seems to calculate runtime dependencies. In particular, it adds
     * fragments' dependencies to the host bundle (see TychoTest#testFragment unit test).
     * This may or may not cause problems...
     *
     * RequiredPluginsClasspathContainer#computePluginEntries has the logic to
     * calculate compile-time dependencies in IDE.
     *
     * TODO find the code used by PDE/Build 
     */
    private static void addBundleAndDependencies(BundleDescription desc, Set<Long> bundleIds, boolean includeOptional) {
        if (desc != null && bundleIds.add(new Long(desc.getBundleId()))) {
            BundleSpecification[] required = desc.getRequiredBundles();
            for (int i = 0; i < required.length; i++) {
                if (includeOptional || !required[i].isOptional())
                    addBundleAndDependencies((BundleDescription)required[i].getSupplier(), bundleIds, includeOptional);
            }
            ImportPackageSpecification[] importedPkgs = desc.getImportPackages();
            for (int i = 0; i < importedPkgs.length; i++) {
                ExportPackageDescription exporter = (ExportPackageDescription)importedPkgs[i].getSupplier();
                // Continue if the Imported Package is unresolved of the package is optional and don't want optional packages
                if (exporter == null || (!includeOptional && Constants.RESOLUTION_OPTIONAL.equals(importedPkgs[i]
                    .getDirective(Constants.RESOLUTION_DIRECTIVE))))
                    continue;
                addBundleAndDependencies(exporter.getExporter(), bundleIds, includeOptional);
            }
            BundleDescription[] fragments = desc.getFragments();
            for (int i = 0; i < fragments.length; i++) {
                if (!fragments[i].isResolved())
                    continue;
                String id = fragments[i].getSymbolicName();
                if (!"org.eclipse.ui.workbench.compatibility".equals(id)) //$NON-NLS-1$
                    addBundleAndDependencies(fragments[i], bundleIds, includeOptional);
            }
            HostSpecification host = desc.getHost();
            if (host != null)
                addBundleAndDependencies((BundleDescription)host.getSupplier(), bundleIds, includeOptional);
        }
    }

    public BundleDescription getBundleDescription(MavenProject project) {
        String location = project.getFile().getParentFile().getAbsolutePath();
        return state.getBundleByLocation(location);
    }

    public BundleDescription getBundleDescription(File location) {
        String absolutePath = location.getAbsolutePath();
        return state.getBundleByLocation(absolutePath);
    }

    private static void setUserProperty(BundleDescription desc, String name, Object value) {
        Object userObject = desc.getUserObject();

        if (userObject != null && !(userObject instanceof Map)) {
            throw new IllegalStateException("Unexpected user object " + desc.toString());
        }

        Map props = (Map)userObject;
        if (props == null) {
            props = new HashMap();
            desc.setUserObject(props);
        }

        props.put(name, value);
    }

    private static Object getUserProperty(BundleDescription desc, String name) {
        Object userObject = desc.getUserObject();
        if (userObject instanceof Map) {
            return ((Map)userObject).get(name);
        }
        return null;
    }

    public MavenProject getMavenProject(BundleDescription desc) {
        return (MavenProject)getUserProperty(desc, PROP_MAVEN_PROJECT);
    }

    public void assertResolved(BundleDescription desc) throws BundleException {
        if (!desc.isResolved()) {
            throw new BundleException("Bundle cannot be resolved: " + desc);
        }
    }

    public String reportErrors(BundleDescription desc) {
        StringBuffer msg = new StringBuffer();
        msg.append("Bundle ").append(desc.getSymbolicName()).append(" cannot be resolved: \n");
        BundleDescription[] bundles = state.getBundles();
        int index = 0;
        for (BundleDescription b : bundles) {
            if (b.isResolved()) {
                continue;
            }
            ResolverError[] errors = state.getResolverErrors(b);
            if (errors.length > 0) {
                msg.append("  ").append("[").append(index++).append("] ").append(b.getSymbolicName()).append("\n");
            }
            for (int i = 0; i < errors.length; i++) {
                ResolverError error = errors[i];
                msg.append("  -->").append(error).append("\n");
            }
        }
        return msg.toString();
    }
   
    public String getManifestAttribute(BundleDescription desc, String attr) {
        Dictionary mf = (Dictionary)getUserProperty(desc, PROP_MANIFEST);
        if (mf != null) {
            return (String)mf.get(attr);
        }
        return null;
    }

    private static File toFile(URL url) {
        if (url.getProtocol().equals("file") == false) {
            return null;
        } else {
            String filename = url.getFile().replace('/', File.separatorChar).replace("%20", " ");
            return new File(filename);
        }
    }
}
TOP

Related Classes of org.apache.geronimo.mavenplugins.osgi.utils.BundleResolver

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.