Package org.apache.aries.application.modelling.impl

Source Code of org.apache.aries.application.modelling.impl.DeployedBundlesImpl

/*
* 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.utils.AppConstants.LOG_ENTRY;
import static org.apache.aries.application.utils.AppConstants.LOG_EXIT;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.aries.application.management.ResolverException;
import org.apache.aries.application.modelling.DeployedBundles;
import org.apache.aries.application.modelling.DeploymentMFElement;
import org.apache.aries.application.modelling.ExportedBundle;
import org.apache.aries.application.modelling.ExportedPackage;
import org.apache.aries.application.modelling.ExportedService;
import org.apache.aries.application.modelling.ImportedBundle;
import org.apache.aries.application.modelling.ImportedPackage;
import org.apache.aries.application.modelling.ImportedService;
import org.apache.aries.application.modelling.ModelledResource;
import org.apache.aries.application.modelling.internal.MessageUtil;
import org.apache.aries.application.modelling.internal.PackageRequirementMerger;
import org.apache.aries.application.utils.AppConstants;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to generate DEPLOYMENT.MF manifest entries for resolved bundles based on
* corresponding APPLICATION.MF entries.
*/
public final class DeployedBundlesImpl implements DeployedBundles
{
  private final Logger logger = LoggerFactory.getLogger(DeployedBundlesImpl.class);
  private final String assetName;

  /** Content from APPLICATION.MF */
  private final Set<ImportedBundle> appContent = new HashSet<ImportedBundle>();
  /** Use Bundle from APPLICATION.MF */
  private final Set<ImportedBundle> appUseBundle = new HashSet<ImportedBundle>();
  /** Content for deployment.mf deployed-content. */
  private final Set<ModelledResource> deployedContent = new HashSet<ModelledResource>();
  /** Content for deployment.mf use-bundle. */
  private final Set<ModelledResource> deployedUseBundle = new HashSet<ModelledResource>();
  /** Content for deployment.mf provision-bundle. */
  private final Set<ModelledResource> deployedProvisionBundle = new HashSet<ModelledResource>();
  /** Content for deployment.mf DeployedImport-Service. */
  private final Collection<ImportedService> deployedImportService = new HashSet<ImportedService>();
  private final Collection<ModelledResource> fakeDeployedBundles = new HashSet<ModelledResource>();
 
  /**
   * Constructor for cases when we have one or more '
   * @param assetName         the name of the asset being deployed.
   * @param appContentNames   the bundle names specified in Deployed-Content.
   * @param appUseBundleNames the bundle names specified in Deployed-Use-Bundle.
   * @param fakeServiceProvidingBundles  bundles that we're pretending are part of the deployed content. Can be null.
   *                                     These bundles are proxies for bundles provided (for example by SCA) that export
   *                                     services matching Application-ImportService.
   */
  public DeployedBundlesImpl(String assetName, Collection<ImportedBundle> appContentNames,
      Collection<ImportedBundle> appUseBundleNames, Collection<ModelledResource> fakeServiceProvidingBundles)
  {
    logger.debug(LOG_ENTRY, "DeployedBundles", new Object[]{appContentNames, appUseBundleNames, fakeServiceProvidingBundles});
   
    this.assetName = assetName;

    appContent.addAll(appContentNames);
    appUseBundle.addAll(appUseBundleNames);
    if (fakeServiceProvidingBundles != null) {
      fakeDeployedBundles.addAll(fakeServiceProvidingBundles);
    }
    logger.debug(LOG_EXIT, "DeployedBundles");
  }
 
  /**
   * Add provisioned version information for a specific bundle name. This will be added to the
   * appropriate manifest header for the specified bundle.
   * @param resolvedBundle the bundle that has been provisioned.
   * @param resolvedVersion the specific version provisioned.
   */
  @Override
  public void addBundle(ModelledResource modelledBundle)
  {
    logger.debug(LOG_ENTRY, "addBundle", new Object[]{modelledBundle});
    // Identify the deployment.mf entries the bundle should be added to by matching
    // both the bundle name and resolved version against the name and version range
    // defined in application.mf.
   
    ExportedBundle resolvedBundle = modelledBundle.getExportedBundle();
   
    if (isBundleMatch(appContent, resolvedBundle))
    {
      logger.debug("Added to " + AppConstants.DEPLOYMENT_CONTENT + ": " + resolvedBundle);
    
      deployedContent.add(modelledBundle);
     
      // Add any service dependencies to the list
      deployedImportService.addAll(modelledBundle.getImportedServices());
    }
    else if (isBundleMatch(appUseBundle, resolvedBundle))
    {
      logger.debug("Added to " + AppConstants.DEPLOYMENT_USE_BUNDLE + ": " + resolvedBundle);
      deployedUseBundle.add(modelledBundle);
    } else
    {
      logger.debug("Added to " + AppConstants.DEPLOYMENT_PROVISION_BUNDLE + ": " + resolvedBundle);
      deployedProvisionBundle.add(modelledBundle);
    }
   logger.debug(LOG_EXIT, "addBundle");   
  }

  /**
   * Check if a match is found between the supplied map of application bundle name/version information,
   * and the supplied bundle name and version.
   * @param imports Imported bundles
   * @param potentialMatch the exported bundle or composite we're interested in
   * @return true if a match is found; otherwise false.
   */
  private boolean isBundleMatch(Set<ImportedBundle> imports, ExportedBundle potentialMatch)
  {
    boolean result = false;
   
    for (ImportedBundle ib : imports)
    {
      if (ib.isSatisfied(potentialMatch))
      {
        result = true;
        break;
      }
    }
 
    return result;
  }
 
  /**
   * Get the value corresponding to the Deployed-Content header in the deployment.mf.
   * @return a manifest entry, or an empty string if there is no content.
   */
  @Override
  public String getContent()
  {
    return createManifestString(deployedContent);
  }
 
  /**
   * Get the value corresponding to the Deployed-Use-Bundle header in the deployment.mf.
   * @return a manifest entry, or an empty string if there is no content.
   */
  @Override
  public String getUseBundle()
  {
    return createManifestString(deployedUseBundle);
  }
 
  /**
   * Get the value corresponding to the Provision-Bundle header in the deployment.mf.
   * @return a manifest entry, or an empty string if there is no content.
   */
  @Override
  public String getProvisionBundle()
  {
    return createManifestString(deployedProvisionBundle);
  }
 
  /**
   * Get the value corresponding to the Import-Package header in the deployment.mf.
   * @return a manifest entry, or an empty string if there is no content.
   * @throws ResolverException if the requirements could not be resolved.
   */
  @Override
  public String getImportPackage() throws ResolverException
  {
    logger.debug(LOG_ENTRY, "getImportPackage");
    Collection<ImportedPackage> externalReqs = new ArrayList<ImportedPackage>(getExternalPackageRequirements());

    //Validate that we don't have attributes that will break until RFC138 is used
    validateOtherImports(externalReqs);
   
    // Find the matching capabilities from bundles in use bundle, and prune
    // matched requirements out of the external requirements collection.
    Map<ImportedPackage,ExportedPackage> useBundlePkgs = new HashMap<ImportedPackage,ExportedPackage>();
    for (Iterator<ImportedPackage> iter = externalReqs.iterator(); iter.hasNext(); )
    {
      ImportedPackage req = iter.next();
      ExportedPackage match = getPackageMatch(req, deployedUseBundle);
      if (match != null)
      {
        useBundlePkgs.put(req, match);
        iter.remove();
      }
    }
   
   
    StringBuilder useBundleImports = new StringBuilder();
    for(Map.Entry<ImportedPackage, ExportedPackage> entry : useBundlePkgs.entrySet()) {
      useBundleImports.append(entry.getValue().toDeploymentString());
      ImportedPackage key = entry.getKey();
      if(key.isOptional())
        useBundleImports.append(";" + Constants.RESOLUTION_DIRECTIVE +":=" + Constants.RESOLUTION_OPTIONAL);
      useBundleImports.append(",");
    }
   
    String result = useBundleImports.toString() + createManifestString(externalReqs);
   
    if(result.endsWith(","))
      result = result.substring(0, result.length() - 1);
    logger.debug(LOG_EXIT, "getImportPackage", result);
    return result;
  }
 
  /**
   * Get the Deployed-ImportService header.
   * this.deployedImportService contains all the service import filters for every
   * blueprint component within the application. We will only write an entry
   * to Deployed-ImportService if
   *   a) the reference isMultiple(), or
   *   b) the service was not available internally when the app was first deployed
   *  
   */
  @Override
  public String getDeployedImportService() {
    logger.debug(LOG_ENTRY,"getDeployedImportService");
    Collection<ImportedService> deployedBundleServiceImports = new ArrayList<ImportedService>();
    Collection<ExportedService> servicesExportedWithinIsolatedContent = new ArrayList<ExportedService>();
    for (ModelledResource mRes : getDeployedContent()) {
      servicesExportedWithinIsolatedContent.addAll(mRes.getExportedServices());
    }
    for (ModelledResource mRes : fakeDeployedBundles) {
      servicesExportedWithinIsolatedContent.addAll(mRes.getExportedServices());
    }
    for (ImportedService impService : deployedImportService) {
      if (impService.isMultiple()) {
        deployedBundleServiceImports.add(impService);
      } else {
        boolean serviceProvidedWithinIsolatedContent = false;
        Iterator<ExportedService> it = servicesExportedWithinIsolatedContent.iterator();
        while (!serviceProvidedWithinIsolatedContent && it.hasNext()) {
          ExportedService svc = it.next();
          serviceProvidedWithinIsolatedContent |= impService.isSatisfied(svc);
        }
        if (!serviceProvidedWithinIsolatedContent) {
          deployedBundleServiceImports.add(impService);
        }
      }
    }
   
    String result = createManifestString(deployedBundleServiceImports);
    logger.debug(LOG_EXIT,"getDeployedImportService", result);
   
    return result;
  }
  /**
   * Get all the requirements of bundles in deployed content that are not satisfied
   * by other bundles in deployed content.
   * @return a collection of package requirements.
   * @throws ResolverException if the requirements could not be resolved.
   */
  private Collection<ImportedPackage> getExternalPackageRequirements()
    throws ResolverException
  {
    logger.debug(LOG_ENTRY,"getExternalPackageRequirements");
   
    // Get all the internal requirements.
    Collection<ImportedPackage> requirements = new ArrayList<ImportedPackage>();
    for (ModelledResource bundle : deployedContent)
    {
      requirements.addAll(bundle.getImportedPackages());
    }
   
    // Filter out requirements satisfied by internal capabilities.
    Collection<ImportedPackage> result = new ArrayList<ImportedPackage>();
    for (ImportedPackage req : requirements)
    {
      ExportedPackage match = getPackageMatch(req, deployedContent);
      //If we didn't find a match then it must come from outside
      if (match == null)
      {
        result.add(req);
      }
    }
   
    PackageRequirementMerger merger = new PackageRequirementMerger(result);
    if (!merger.isMergeSuccessful())
    {
      List<String> pkgNames = new ArrayList<String>(merger.getInvalidRequirements());
     
      StringBuilder buff = new StringBuilder();
      for (String pkgName : merger.getInvalidRequirements())
      {
        buff.append(pkgName).append(", ");
      }

      int buffLen = buff.length();
      String pkgString = (buffLen > 0 ? buff.substring(0, buffLen - 2) : "");

      ResolverException re = new ResolverException(MessageUtil.getMessage(
          "INCOMPATIBLE_PACKAGE_VERSION_REQUIREMENTS", new Object[] { assetName, pkgString }));
      re.setUnsatisfiedRequirements(pkgNames);
      logger.debug(LOG_EXIT,"getExternalPackageRequirements", re);
     
      throw re;
    }
   
    result = merger.getMergedRequirements();
    logger.debug(LOG_EXIT,"getExternalPackageRequirements", result);
   
    return result;
  }
 
  /**
   * Create entries for the Import-Package header corresponding to the supplied
   * packages, referring to bundles not in Use-Bundle.
   * @param requirements packages for which entries should be created.
   * @return manifest header entries.
   * @throws ResolverException if the imports are invalid.
   */
  private void validateOtherImports(Collection<ImportedPackage> requirements)
    throws ResolverException
  {
    logger.debug(LOG_ENTRY, "validateOtherImports", requirements);
    for (ImportedPackage req : requirements)
    {
      String pkgName = req.getPackageName();

      for (String name : req.getAttributes().keySet())
      {
        if (Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE.equals(name)
            || Constants.BUNDLE_VERSION_ATTRIBUTE.equals(name))
        {
          ResolverException re = new ResolverException(MessageUtil.getMessage(
              "INVALID_PACKAGE_REQUIREMENT_ATTRIBUTES", new Object[] { assetName, name, pkgName }));
          re.setUnsatisfiedRequirements(Arrays.asList(pkgName));
          logger.debug(LOG_EXIT, "validateOtherImports", re);
          throw re;
        }
      }
    }
    logger.debug(LOG_EXIT, "validateOtherImports");
  }

  /**
   * Get a package match between the specified requirement and a capability of the supplied
   * bundles. The resulting match object might not refer to any matching capability.
   * @param requirement the {@link ImportedPackageImpl} to be matched.
   * @param bundles the bundles to be searched for matching capabilities.
   * @return an ExportedPackageImpl or null if no match is found.
   */
  private ExportedPackage getPackageMatch(ImportedPackage requirement, Collection<ModelledResource> bundles)
  {
    logger.debug(LOG_ENTRY, "getPackageMatch", new Object[]{requirement, bundles});
    ExportedPackage result = null;
   
    outer: for (ModelledResource bundle : bundles)
    {
      for (ExportedPackage pkg : bundle.getExportedPackages())
      {
        if(requirement.isSatisfied(pkg)) {
          result = pkg;
          break outer;
        }
      }
    }
    logger.debug(LOG_EXIT, "getPackageMatch", new Object[]{result});
    return result;
  }
 
  private String createManifestString(Collection<? extends DeploymentMFElement> values)
  {
    logger.debug(LOG_ENTRY, "createManifestString", new Object[]{values});
    StringBuilder builder = new StringBuilder();
    for (DeploymentMFElement value : values)
    {
      builder.append(value.toDeploymentString()).append(",");
    }
   
    int length = builder.length();
    String result = (length > 0 ? builder.substring(0, length - 1) : "");
    logger.debug(LOG_EXIT, "createManifestString", new Object[]{result});
    return result;
  }
 

  @Override
  public String toString()
  {
    return AppConstants.DEPLOYMENT_CONTENT + '=' + deployedContent + ' ' +
        AppConstants.DEPLOYMENT_USE_BUNDLE + '=' + deployedUseBundle + ' ' +
        AppConstants.DEPLOYMENT_PROVISION_BUNDLE + '=' + deployedProvisionBundle;
  }
 
  /**
   * Get the set of bundles that are going to be deployed into an isolated framework
   * @return a set of bundle metadata
   */
  @Override
  public Collection<ModelledResource> getDeployedContent()
  {
    logger.debug(LOG_ENTRY, "getDeployedContent");
    logger.debug(LOG_EXIT,"getDeployedContent", deployedContent);
    return Collections.unmodifiableCollection(deployedContent);
  }
 
  /**
   * Get the set of bundles that map to Provision-Bundle: these plus
   * getRequiredUseBundle combined give the bundles that will be provisioned
   * into the shared bundle space
   * 'getProvisionBundle' returns the manifest header string, so this method
   * needs to be called something else.
   *
   */
  @Override
  public Collection<ModelledResource> getDeployedProvisionBundle ()
  {
    logger.debug(LOG_ENTRY,"getDeployedProvisionBundle");
    logger.debug(LOG_EXIT, "getDeployedProvisionBundle", deployedContent);
    return Collections.unmodifiableCollection(deployedProvisionBundle);
  }
 
  /**
   * Get the subset of bundles specified in use-bundle that are actually required to
   * satisfy direct requirements of deployed content.
   * @return a set of bundle metadata.
   * @throws ResolverException if the requirements could not be resolved.
   */
  @Override
  public Collection<ModelledResource> getRequiredUseBundle() throws ResolverException
  {
    logger.debug(LOG_ENTRY, "getRequiredUseBundle");
    Collection<ImportedPackage> externalReqs = getExternalPackageRequirements();
    Collection<ModelledResource> usedUseBundles = new HashSet<ModelledResource>();
    for (ImportedPackage req : externalReqs)
    {
      // Find a match from the supplied bundle capabilities.
      ExportedPackage match = getPackageMatch(req, deployedUseBundle);
      if (match != null)
      {
          usedUseBundles.add(match.getBundle());
      }
    }
    logger.debug(LOG_EXIT, "getRequiredUseBundle", usedUseBundles);
    return usedUseBundles;
  }

  /** This method will be overridden by a PostResolveTransformer returning an extended version of
   * DeployedBundles
   */
  @Override
  public Map<String, String> getExtraHeaders() {
    logger.debug (LOG_ENTRY, "getExtraHeaders");
    Map<String, String> result = Collections.emptyMap();
    logger.debug (LOG_EXIT, "getExtraHeaders", result);
    return result;
  }
}
TOP

Related Classes of org.apache.aries.application.modelling.impl.DeployedBundlesImpl

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.