Package fr.imag.adele.obrMan.internal

Source Code of fr.imag.adele.obrMan.internal.OBRManager

/**
* Copyright 2011-2012 Universite Joseph Fourier, LIG, ADELE team
*   Licensed 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 fr.imag.adele.obrMan.internal;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import fr.imag.adele.apam.CST;
import fr.imag.adele.apam.Component;
import fr.imag.adele.apam.CompositeType;
import fr.imag.adele.apam.DeploymentManager;
import fr.imag.adele.apam.Implementation;
import fr.imag.adele.apam.RelToResolve;
import fr.imag.adele.apam.Resolved;
import fr.imag.adele.apam.declarations.ComponentKind;
import fr.imag.adele.apam.declarations.references.components.ComponentReference;
import fr.imag.adele.apam.declarations.references.resources.InterfaceReference;
import fr.imag.adele.apam.declarations.references.resources.MessageReference;
import fr.imag.adele.apam.declarations.references.resources.ResourceReference;
import fr.imag.adele.apam.util.ApamFilter;

/**
* This manager handles OBR request for a specific composite.
*
* Resolution in this context is based on repositories specified in the OBR manager
* model associated with the context in APAM
*
*
* @author vega
*
*/
public class OBRManager {

  /**
   * The context this manager handles
   */
  private final CompositeType context;

  /**
   * The global obr manager
   */
  private final OBRMan obrMan;

  /**
   * The model associated with this composite
   */
  private final Model model;


  public OBRManager(OBRMan obrman, CompositeType context, Model model) {
    this.obrMan   = obrman;
    this.context  = context;
    this.model    = model;
  }
 
  public Model getModel() {
    return model;
  }

  public String getName() {
    return context.getName();
  }

  public Resolved<?> resolve(RelToResolve relation) {
   
    /*
     * Try to optimize the case of finding a component by name, without constraints.
     *
     */
    ComponentReference<?> reference = relation.getTarget().as(ComponentReference.class);
    if (reference != null && reference.getKind() == relation.getTargetKind()) {
      DeployableComponent component = getComponent(reference);
      return component != null ? new Resolved<Component>(component.install(this)) : null;
    }
   
    /*
     * This is the case of relation resolution, based on searching for components providing
     * the specified resources
     */
   
    Set<DeployableComponent> candidates = getProviders(relation);

    if (candidates.isEmpty())
      return null;
   
    /*
     * When the target kind is INSTANCE we must consider a special case, as the calculated candidate
     * providers are implementations.
     *
     * We must check if there are deployable instances of these implementations declared in the
     * repository, as these are the real candidates.
     *
     */
    boolean instantiableCandidates = (relation.getTargetKind() == ComponentKind.INSTANCE);
   
    if (instantiableCandidates) {
     
      /*
       * Select a single revision of each implementation
       *
       * TODO Because the revision is based uniquely based on the version number, it is not possible to use preferences
       * to select the preferred version. A better alternative would be to use preferences as a ranking, and use version
       * number to break ties.
       */
      selectRevisions(candidates);

      StringBuffer providerInstancesQuery = new StringBuffer();
     
      providerInstancesQuery.append("(|");
      for (DeployableComponent candidate : candidates) {
        if (candidate.getReference().getKind() == ComponentKind.IMPLEMENTATION)
          providerInstancesQuery.append("(").append(CST.IMPLNAME).append("=").append(candidate.getReference().getName()).append(")");
      }
      providerInstancesQuery.append(")");
     
      ApamFilter query = ApamFilter.newInstance(providerInstancesQuery.toString());
     
      /*
       * TODO We can not be sure that the candidate instance corresponds to the selected revision of the implementation. We
       * are just assuming that instances can be created without considering versions, this may not be true if the implementation
       * declaration has changed in a not backward compatible way
       */
      Set<DeployableComponent> providerInstances = new HashSet<DeployableComponent>();
      for (DeployableComponent component : obrMan.getComponents(this)) {
        if (component.getReference().getKind() == ComponentKind.INSTANCE &&
          component.satisfies(query) && component.satisfies(relation))
          providerInstances.add(component);
      }
     
      if (! providerInstances.isEmpty()) {
        candidates = providerInstances;
        instantiableCandidates = false;
      }
       
    }

    /*
     * Select a single revision of each candidate
     *
     * TODO Because the revision is based uniquely based on the version number, it is not possible to use preferences
     * to select the preferred version. A better alternative would be to use preferences as a ranking, and use version
     * number to break ties.
     */
    selectRevisions(candidates);

    /*
     * Select best candidate according to preferences
     *
     * TODO currently if the target kind is INSTANCE and there are both implementation and instance preferences, only one
     * set is evaluated. A better alternative would be to use both criteria for ranking.
     */
    if (relation.hasPreferences() && !relation.isMultiple()) {
     
      int bestRanking       = 0;
      DeployableComponent best   = null;
     
      for (DeployableComponent candidate : candidates) {
       
        int ranking = candidate.ranking(relation);
        if (ranking >= bestRanking) {
          best    = candidate;
          bestRanking = ranking;
        }
      }
     
      candidates = Collections.singleton(best);
    }
   
    /*
     * Deploy and install candidates
     *
     * TODO currently we only install a single candidate, irrespective of the multiplicity of the relationship.
     * This is a trade-off between comprehensiveness and laziness (in the worst case scenario, when there is no
     * constraints, we would install all possible providers) we should have an explicit strategy.
     */
   
    Component selected = candidates.iterator().next().install(this);
   
    return !instantiableCandidates ? new Resolved<Component>(selected) : new Resolved<Implementation>((Implementation)selected,true);
  }

  /**
   * Look for a specific component in the component repository.
   *
   * Notice that a component is searched by the specified name and kind of the reference. However,
   * several versions may exist , in this case we choose the latest version.
   *
   * TODO Should we consider preferences when selecting the best revision ? A possible implementation
   * would be to order according to preferences, and use version number to break ties.
   *
   */
  private DeployableComponent getComponent(ComponentReference<?> searched) {

    boolean ignoreKind      = searched.getKind() == ComponentKind.COMPONENT;
    DeployableComponent result  = null;
     
    for (DeployableComponent component : obrMan.getComponents(this)) {
      if  ( (component.getReference().equals(searched)) &&
          (ignoreKind || component.getReference().getKind() == searched.getKind()) ) {
       
        result = (result == null || component.isPreferedVersionThan(result)) ? component : result;
      }
    }
   
    return result;
     
  }

  /**
   * Look for components that provide the resource specified in the relation
   *
   */
  private Set<DeployableComponent> getProviders(RelToResolve relation) {

   
    /*
     * Depending on the kind requested resource, we calculate the filter that must be satisfied
     * by the providing components.
     *
     * For java resources and specifications the metadata contains all the information to find the
     * provider components. For specific component references we search by name.
     */
    String requirementQuery = null;
   
    ResourceReference requestedResource = relation.getTarget().as(ResourceReference.class);
    if (requestedResource != null) {
     
      if (requestedResource instanceof InterfaceReference) {
        requirementQuery = "("+CST.PROVIDE_INTERFACES+"*>" + requestedResource.getJavaType() + ")";
      }
     
      else if (requestedResource instanceof MessageReference) {
        requirementQuery = "("+CST.PROVIDE_MESSAGES+"*>" + requestedResource.getJavaType() + ")";
      }
     
    }
   
    ComponentReference<?> requestedComponent = relation.getTarget().as(ComponentReference.class);
    if (requestedComponent != null) {
     
      if (requestedComponent.getKind() == ComponentKind.SPECIFICATION) {
        requirementQuery = "("+CST.PROVIDE_SPECIFICATION+"*>" + requestedComponent.getName() + ")";
      }

      else if (requestedComponent.getKind() == ComponentKind.IMPLEMENTATION) {
        requirementQuery = "("+CST.IMPLNAME+ "=" + requestedComponent.getName() + ")";
      }

      else if (requestedComponent.getKind() == ComponentKind.COMPONENT) {
        requirementQuery ="("+CST.NAME+ "=" + requestedComponent.getName() + ")";
      }

    }
   
    if (requirementQuery == null)
      return Collections.emptySet();

   
    /*
     * We need to do an special case when the target kind is INSTANCE, as the metadata of instances doesn't contain
     * the provided resources, so we search for corresponding implementations.
     *
     * The caller should decide if it look for instances in the repository or instantiate the found implementations.
     */
    ComponentKind searchedKind     = relation.getTargetKind();
    if (searchedKind == ComponentKind.INSTANCE)
      searchedKind = ComponentKind.IMPLEMENTATION;
   
    ApamFilter requirement = ApamFilter.newInstance(requirementQuery);
   
    Set<DeployableComponent> candidates = new HashSet<DeployableComponent>();
    for (DeployableComponent component : obrMan.getComponents(this)) {
     
      /*
       * Ignore all components that do not provide the required resource
       */
      if (component.getReference().getKind() != searchedKind || ! component.satisfies(requirement))
        continue;
     
      /*
       * Evaluate constraints specified in the relationship
       */
      if (component.satisfies(relation))
        candidates.add(component);
     
    }
   
    /*
     * keep a single version of each candidate
     */
    return candidates;

  }
 

  /**
   * Verify if this manager deployed the specified component, and return a newer version that can be used
   * to update it.
   *
   */
  public DeploymentManager.Unit getDeploymentUnit(Component deployed) {

    DeployableComponent result  = null;
   
    for (DeployableComponent component : obrMan.getComponents(this)) {
      if  (component.isRepositoryVersionOf(deployed)) {
        result = (result == null || component.isPreferedVersionThan(result)) ? component : result;
      }
    }
   
    return result != null ? new DeploymentUnit(this,result,deployed) : null;

  }

  /**
   * This represents a component that has been deployed in the system by obrman, and that can be
   * updated from the repository
   */
  private static class DeploymentUnit implements DeploymentManager.Unit {

    private final Component       component;
    private final DeployableComponent  repositoryVersion;
    private final OBRManager       context;
   
    public DeploymentUnit(OBRManager context, DeployableComponent repositoryVersion, Component component) {

      this.context      = context;
      this.component      = component;
      this.repositoryVersion  = repositoryVersion;
    }
   
   
    @Override
    public Set<String> getComponents() {
      return repositoryVersion.getDeploymentUnitComponents();
    }

    @Override
    public void update() throws Exception {
      repositoryVersion.update(context,component);
     
    }
   
  }
 
 
  /**
   *  Utility method to keep a single revision of each component in the given set.
   *
   */
  private static void selectRevisions(Set<DeployableComponent> components) {
   
    Map<ComponentReference<?>,DeployableComponent> retained = new HashMap<ComponentReference<?>,DeployableComponent>();
   
    for (DeployableComponent component : components) {
     
      DeployableComponent best = retained.get(component.getReference());
     
      if (best == null || component.isPreferedVersionThan(best))
        retained.put(component.getReference(),component);
    }
   
    components.retainAll(retained.values());
  }
}
TOP

Related Classes of fr.imag.adele.obrMan.internal.OBRManager

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.