/* * 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.modelling.impl; import static org.apache.aries.application.modelling.ModellingConstants.OPTIONAL_KEY; import static org.apache.aries.application.modelling.ResourceType.PACKAGE; import static org.apache.aries.application.utils.AppConstants.LOG_ENTRY; import static org.apache.aries.application.utils.AppConstants.LOG_EXIT; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Map.Entry; import org.apache.aries.application.InvalidAttributeException; import org.apache.aries.application.modelling.ExportedPackage; import org.apache.aries.application.modelling.ImportedPackage; import org.apache.aries.application.modelling.Provider; import org.apache.aries.application.modelling.ResourceType; import org.apache.aries.application.modelling.utils.impl.ModellingHelperImpl; import org.apache.aries.application.utils.FilterUtils; import org.apache.aries.util.manifest.ManifestHeaderProcessor; import org.osgi.framework.Constants; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.Version; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * An imported, or required package. Capable of generating an entry in DEPLOYMENT.MF's Import-Package header. */ public class ImportedPackageImpl implements ImportedPackage { private final boolean _optional; private final String _filterString; private final Filter _filter; private final String _package; private final String _versionRange; private final Map<String,String> _attributes; private final Logger logger = LoggerFactory.getLogger(ImportedPackageImpl.class); /** * Construct a package requirement * @param pkg The name of the required package * @param attributes Other attributes - most commonly, version * @throws InvalidAttributeException */ public ImportedPackageImpl (String pkg, Map<String, String> attributes) throws InvalidAttributeException { logger.debug(LOG_ENTRY, "ImportedPackageImpl", new Object[] {pkg, attributes}); _package = pkg; String versionRange = null; if (attributes != null) { _optional = (Constants.RESOLUTION_OPTIONAL.equals(attributes.get(OPTIONAL_KEY))); versionRange = attributes.get(Constants.VERSION_ATTRIBUTE); _attributes = new HashMap<String, String>(attributes); } else { _optional = false; _attributes = new HashMap<String, String>(); } if (versionRange == null) { _versionRange = Version.emptyVersion.toString(); } else { _versionRange = versionRange; } _attributes.put(Constants.VERSION_ATTRIBUTE, _versionRange); _filterString = ManifestHeaderProcessor.generateFilter(PACKAGE.toString(), _package, _attributes); try { _filter = FrameworkUtil.createFilter(FilterUtils.removeMandatoryFilterToken(_filterString)); } catch (InvalidSyntaxException isx) { logger.debug(LOG_EXIT, "ImportedPackageImpl", new Object[] {isx}); throw new InvalidAttributeException(isx); } logger.debug(LOG_EXIT, "ImportedPackageImpl"); } /** * Get this ImportedPackageImpl's attributes * @return attributes */ public Map<String, String> getAttributes() { logger.debug(LOG_ENTRY, "getAttributes"); logger.debug(LOG_EXIT, "getAttributes", new Object[] {_attributes}); return Collections.unmodifiableMap(_attributes); } /** * Get the package name * @return package name */ public String getPackageName() { logger.debug(LOG_ENTRY, "getPackageName"); logger.debug(LOG_EXIT, "getPackageName", new Object[] {_package}); return _package; } /** * Get the imported package's version range * @return version range */ public String getVersionRange() { logger.debug(LOG_ENTRY, "getVersionRange"); logger.debug(LOG_EXIT, "getVersionRange", new Object[] {_versionRange}); return _versionRange; } public String getAttributeFilter() { logger.debug(LOG_ENTRY, "getAttributeFilter"); logger.debug(LOG_EXIT, "getAttributeFilter", new Object[] {_filterString}); return _filterString; } public ResourceType getType() { logger.debug(LOG_ENTRY, "getType"); logger.debug(LOG_EXIT, "getType", new Object[] {PACKAGE}); return PACKAGE; } public boolean isMultiple() { logger.debug(LOG_ENTRY, "isMultiple"); logger.debug(LOG_EXIT, "isMultiple", new Object[] {false}); return false; // cannot import a given package more than once } public boolean isOptional() { logger.debug(LOG_ENTRY, "isOptional"); logger.debug(LOG_EXIT, "isOptional", new Object[] {_optional}); return _optional; } public boolean isSatisfied(Provider capability) { logger.debug(LOG_ENTRY, "isSatisfied", new Object[]{capability}); if (capability.getType() != PACKAGE) { logger.debug(LOG_EXIT, "isSatisfied", new Object[] {false}); return false; } Dictionary<String, Object> dict = new Hashtable<String, Object> (capability.getAttributes()); String version = (String) dict.get(Constants.VERSION_ATTRIBUTE); if (version != null) { dict.put(Constants.VERSION_ATTRIBUTE, Version.parseVersion(version)); } boolean allPresent = ModellingHelperImpl.areMandatoryAttributesPresent_(_attributes, capability); boolean result = allPresent && _filter.match(dict); logger.debug(LOG_EXIT, "isSatisfied", new Object[] {result}); return result; } /** * This method turns an {@link ImportedPackageImpl} into a string suitable for a * Provision-Bundle style package import. * It will not include ;bundle-symbolic-name=bundleName;bundle-version=version attribute pairs * @return A String */ @SuppressWarnings("deprecation") public String toDeploymentString() { logger.debug(LOG_ENTRY, "toDeploymentString"); StringBuilder sb = new StringBuilder(_package); // Note that the RESOLUTION_DIRECTIVE is set in this map, so it will be // output automatically. p41 of the OSGi Core Spec v4.2 includes an example // Import-Package with a resolution:=mandatory directive on. We could choose to suppress // resolution:=mandatory on packages however, since mandatory is the default. for (Map.Entry<String, String> entry : _attributes.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (!key.equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE) && !key.equals(Constants.BUNDLE_VERSION_ATTRIBUTE) && !key.equals(PACKAGE.toString()) && !key.equals(Constants.PACKAGE_SPECIFICATION_VERSION)) { sb.append(";").append(key).append("=\"").append(value).append('"'); } else { logger.debug("ignoring attribute {" + key + "=" + value + "} in ImportedPackageImpl.toDeploymentString()"); } } String result = sb.toString(); logger.debug(LOG_EXIT, "toDeploymentString", new Object[]{result}); return result; } @Override public String toString() { return toDeploymentString(); } @Override public boolean equals(Object thing) { if (thing == this) { return true; } else { if (thing instanceof ImportedPackage) { ImportedPackage otherPackage = (ImportedPackage) thing; if (!this.getPackageName() .equals(otherPackage.getPackageName())) { return false; } if (!this.getVersionRange().equals(otherPackage.getVersionRange())) { return false; } if (!this.getPackageName() .equals(otherPackage.getPackageName())) { return false; } Map<String, String> otherAttributes = otherPackage .getAttributes(); // Ignore the bundle, since the same package imported from // different bundles should count as the same if (!attributesAreEqual(otherAttributes)) { return false; } } return true; } } private boolean attributesAreEqual(Map<String, String> otherAttributes) { // We better have the same number of attributes if (this.getAttributes().size() != otherAttributes.size()) { return false; } for (Entry<String, String> entry : getAttributes().entrySet()) { String key = entry.getKey(); if (otherAttributes != null && otherAttributes.containsKey(key)) { Object otherValue = otherAttributes.get(key); Object value = entry.getValue(); if (value == null) { if (otherValue != null) { return false; } } else { if (!value.equals(otherValue)) { return false; } } } else { // We insist every value be present on both sides return false; } } return true; } @Override public int hashCode() { return getPackageName().hashCode(); } }