/* * 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 WARRANTIESOR 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.aries.application.runtime.framework.utils; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.aries.application.Content; import org.apache.aries.application.utils.manifest.ContentFactory; import org.apache.aries.util.VersionRange; import org.apache.aries.util.manifest.ManifestHeaderProcessor; import org.apache.aries.util.manifest.ManifestHeaderProcessor.NameValuePair; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.Version; public class EquinoxFrameworkUtils { public static Collection<Content> getExportPackages(BundleContext isolatedBundleContext) { Set<Content> exports = new HashSet<Content>(); Bundle sysBundle = isolatedBundleContext.getBundle(0); if (sysBundle != null && sysBundle.getHeaders() != null) { String exportString = (String) sysBundle.getHeaders().get(Constants.EXPORT_PACKAGE); if (exportString != null) { for (NameValuePair nvp : ManifestHeaderProcessor .parseExportString(exportString)) exports.add(ContentFactory.parseContent(nvp.getName(), nvp.getAttributes())); } } return Collections.unmodifiableSet(exports); } public static Collection<Content> getSystemExtraPkgs(BundleContext context) { Set<Content> extraPkgs = new HashSet<Content>(); String exportString = context.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA); if (exportString != null) { for (NameValuePair nvp : ManifestHeaderProcessor .parseExportString(exportString)) extraPkgs.add(ContentFactory.parseContent(nvp.getName(), nvp.getAttributes())); } return Collections.unmodifiableSet(extraPkgs); } public static Collection<Content> calculateImports(final Collection<Content> importPackage, final Collection<Content> exportPackages) { Set<Content> results = new HashSet<Content>(); if (importPackage != null && !importPackage.isEmpty()) { for (Content exportPkg : exportPackages) { for (Content importPkg : importPackage) { if (!(importPkg.getContentName().equals(exportPkg.getContentName()) && importPkg.getVersion().equals(exportPkg.getVersion()))) { results.add(importPkg); } } } } return Collections.unmodifiableSet(results); } public static String contentToString(Content content) { StringBuffer value = new StringBuffer(); value.append(content.getContentName()); Map<String, String> nvm = content.getNameValueMap(); for (Map.Entry<String, String> entry : nvm.entrySet()) { if (entry.getKey().equalsIgnoreCase(Constants.VERSION_ATTRIBUTE) || entry.getKey().equalsIgnoreCase(Constants.BUNDLE_VERSION_ATTRIBUTE)) { value.append(";" + entry.getKey() + "=\"" + entry.getValue() + "\""); } else { value.append(";" + entry.getKey() + "=" + entry.getValue()); } } return value.toString(); } /** * Calculates which system packages should be flowed * to a child framework based on what packages the * child framework imports. Equinox will require anything imported by the * child framework which is available from the system bundle * in the parent framework to come from the system bundle * in the child framework. However, we don't want to flow * all the extra system packages by default since we want CBAs * which use them to explicitly import them. * @param importPackage * @return * @throws CompositeBundleCalculateException */ public static String calculateSystemPackagesToFlow(final Collection<Content> systemExports, final Collection<Content> imports) { // Let's always set javax.transaction as system extra packages because of the split package. // It is reasonable to do so because we always flow userTransaction service into child framework anyway. Map<String, String> map = new HashMap<String, String>(); map.put(EquinoxFrameworkConstants.TRANSACTION_BUNDLE, EquinoxFrameworkConstants.TRANSACTION_BUNDLE_VERSION); Map<String, Map<String, String>> resultMap = new HashMap<String, Map<String, String>>(); resultMap.put(EquinoxFrameworkConstants.TRANSACTION_BUNDLE, map); // let's go through the import list to build the resultMap for (Content nvp : imports) { String name = nvp.getContentName().trim(); // if it exist in the list of packages exported by the system, we need to add it to the result if (existInExports(name, nvp.getNameValueMap(), systemExports)) { /* We've now ensured the versions match, but not worried too * much about other constraints like company or any of the * other things which could be added to a version statement. * We don't want to flow system packages we don't need to, * but we're not in the business of provisioning, so we'll * let OSGi decide whether every constraint is satisfied * and resolve the bundle or not, as appropriate. */ for (Content nvpp : systemExports) { if (nvpp.getContentName().trim().equals(name)) { Map<String, String> frameworkVersion = nvpp.getNameValueMap(); resultMap.put(name, frameworkVersion); // We don't break here since we're too lazy to check the version // again and so we might end up flowing multiple statements for the // same package (but with different versions). Better this than // accidentally flowing the wrong version if we hit it first. } } } } StringBuffer result = new StringBuffer(); for (String key : resultMap.keySet()) { result.append(getString(key, resultMap) + ","); } String toReturn = trimEndString(result.toString().trim(), ","); return toReturn; } /** * check if the value in nvm already exist in the exports * @param key * @param nvm * @param exports * @return boolean whether the value in nvm already exist in the exports */ private static boolean existInExports(String key, Map<String, String> nvm, final Collection<Content> exports) { boolean value = false; for (Content nvp : exports) { if (nvp.getContentName().trim().equals(key.trim())) { // ok key equal. let's check the version // if version is higher, we still want to import, for example javax.transaction;version=1.1 String vi = nvm.get(Constants.VERSION_ATTRIBUTE); String ve = nvp.getNameValueMap().get(Constants.VERSION_ATTRIBUTE); if (vi == null || vi.length() == 0) { vi = "0.0.0"; } if (ve == null || ve.length() == 0) { ve = "0.0.0"; } if (vi.indexOf(",") == -1) { if (new Version(vi).compareTo(new Version(ve)) <= 0) { // we got it covered in our exports value = true; } } else { // parse vi into version range. VersionRange vri = ManifestHeaderProcessor.parseVersionRange(vi); Version minV = vri.getMinimumVersion(); Version maxV = vri.getMaximumVersion(); if (minV.compareTo(new Version(ve)) < 0 && maxV.compareTo(new Version(ve)) > 0) { value = true; } else if (minV.compareTo(new Version(ve)) == 0 && !!!vri.isMinimumExclusive()) { value = true; } else if (maxV.compareTo(new Version(ve)) == 0 && !!!vri.isMaximumExclusive()) { value = true; } } } } return value; } private static String trimEndString(String s, String trim) { if (s.startsWith(trim)) { s = s.substring(trim.length()); } if (s.endsWith(trim)) { s = s.substring(0, s.length() - trim.length()); } return s; } private static String getString(String key, Map<String, Map<String, String>> imports) { StringBuffer value = new StringBuffer(); value.append(key); Map<String, String> nvm = imports.get(key); for (Map.Entry<String, String> entry : nvm.entrySet()) { if (entry.getKey().equalsIgnoreCase(Constants.VERSION_ATTRIBUTE) || entry.getKey().equalsIgnoreCase(Constants.BUNDLE_VERSION_ATTRIBUTE)) { value.append(";" + entry.getKey() + "=\"" + entry.getValue() + "\""); } else { value.append(";" + entry.getKey() + "=" + entry.getValue()); } } return value.toString(); } }