summaryrefslogtreecommitdiffstats
path: root/sca-java-1.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleResolver.java
diff options
context:
space:
mode:
Diffstat (limited to 'sca-java-1.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleResolver.java')
-rw-r--r--sca-java-1.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleResolver.java490
1 files changed, 490 insertions, 0 deletions
diff --git a/sca-java-1.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleResolver.java b/sca-java-1.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleResolver.java
new file mode 100644
index 0000000000..06f26f7e65
--- /dev/null
+++ b/sca-java-1.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleResolver.java
@@ -0,0 +1,490 @@
+/*
+ * 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.tuscany.sca.tools.maven.compiler.osgi;
+
+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 null;
+ }
+ 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().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:
+ case REQUIRE_BUNDLE_USES_CONFLICT:
+ default:
+ logger.error(reportErrors(bundle));
+ 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);
+ }
+ }
+
+ /*
+ public static void main(String[] args) throws Exception {
+ BundleResolver resolver = new BundleResolver(new ConsoleLogger(Logger.LEVEL_INFO, "tuscany"));
+
+ String home = System.getProperty("user.home");
+ File jar =
+ new File(new File(home),
+ ".m2/repository/org/apache/tuscany/sca/tuscany-sca-api/1.4-EQUINOX-SNAPSHOT/tuscany-sca-api-1.4-EQUINOX-SNAPSHOT.jar");
+ BundleDescription bundle = resolver.addBundle(jar);
+ resolver.resolveState();
+ resolver.assertResolved(bundle);
+ }
+ */
+}