Package fr.imag.adele.apam.impl

Source Code of fr.imag.adele.apam.impl.PendingRequest

/**
* 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.apam.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import fr.imag.adele.apam.ApamResolver;
import fr.imag.adele.apam.CST;
import fr.imag.adele.apam.Component;
import fr.imag.adele.apam.Composite;
import fr.imag.adele.apam.Instance;
import fr.imag.adele.apam.Link;
import fr.imag.adele.apam.RelationDefinition;
import fr.imag.adele.apam.Resolved;
import fr.imag.adele.apam.apform.Apform2Apam;
import fr.imag.adele.apam.declarations.ComponentKind;
import fr.imag.adele.apam.declarations.references.components.ComponentReference;
import fr.imag.adele.apam.declarations.references.resources.ResourceReference;

/**
* This class is used to represent the pending requests that are waiting for
* resolution.
*
*
*/
public class PendingRequest extends Apform2Apam.PendingThread {

  /**
   * The source of the relation
   */
  protected final Component source;

  /**
   * The relation to resolve
   */
  protected final RelationDefinition relDef;

  /**
   * The composite in which context the resolution will be performed
   */
  protected final Composite context;

  /**
   * The resolver
   */
  protected final ApamResolver resolver;

  /**
   * The result of the resolution
   */
  private Resolved<?> resolution;

  /**
   * Whether this request is being resolved in some thread
   */
  private boolean isResolving = false;

  /**
   * Whether the thread that created this request is blocked
   */
  private boolean isBlocked = false;

  /**
   * The stack of the blocked requests
   */
  private List<StackTraceElement> stack;
 
  /**
   * Whether this request has been disposed, this happen for instance when the
   * source component is removed
   */
  private boolean isDisposed = false;

  private static ThreadLocal<PendingRequest> current = new ThreadLocal<PendingRequest>();

  /**
   * The request that is being resolved by the current thread
   */
  public static PendingRequest current() {
    return current.get();
  }

  /**
   * Whether the current thread is performing a resolution retry
   */
  public static boolean isRetry() {
    return current() != null;
  }

  /**
   * The stack of the request executing in the context of the current thread.
   *
   */
  private static List<StackTraceElement> getCurrentStack() {

    List<StackTraceElement> stack = new ArrayList<StackTraceElement>(Arrays.asList(new Throwable().getStackTrace()));

    /*
     * Remove APAM implementtaion frameworks from the top of the stack, to increase the readability
     * of the stack trace
     */
    Iterator<StackTraceElement> frames = stack.iterator();
    while (frames.hasNext()) {
      if (frames.next().getClassName().startsWith(PendingRequest.class.getPackage().getName())) {
        frames.remove();
        continue;
      }

      break;
    }
    return stack;
  }

  /**
   * Builds a new pending request reification
   */
  public PendingRequest(ApamResolver resolver, Component source, RelationDefinition relDef) {
   
    super(source.toString());
   
    this.resolver = resolver;

    this.source = source;
    this.context = (source instanceof Instance) ? ((Instance) source).getComposite() : CompositeImpl.getRootAllComposites();
    this.relDef = relDef;

    this.resolution = null;
  }

  @Override
  public String getCondition() {
    return relDef.toString();
  }

  private synchronized void beginResolve() {
    current.set(this);
    isResolving = true;
    resolution = null;
  }

  /**
   * Block the current thread until a component satisfying the request is
   * available.
   *
   * Resolution must be retried by another thread, and when successful it will
   * notify this object to unblock the waiting thread.
   *
   */
  public void block() {
    synchronized (this) {
      try {
        /*
         * wait for resolution
         */

        isBlocked = true;
        stack = getCurrentStack();

        while (!isResolved()) {
          this.wait();
        }

        isBlocked = false;
        stack = null;
      } catch (InterruptedException ignored) {
      }
    }
  }
 
  /**
   * The stack trace for blocked requests
   */
  public List<StackTraceElement> getStack() {
    return stack;
  }


  public synchronized void dispose() {
    isDisposed = true;
    this.notifyAll();
  }

  private synchronized void endResolve(Resolved<?> resolverResult) {
    isResolving = false;
    resolution = resolverResult;
    current.set(null);

    this.notifyAll();
  }

  /**
   * The context in which the resolution is requested
   */
  public Composite getContext() {
    return context;
  }

  /**
   * The relation that needs resolution
   */
  public RelationDefinition getRelation() {
    return relDef;
  }

  /**
   * The result of the resolution
   */
  public synchronized Resolved<?> getResolution() {
    return resolution;
  }

  public Component getSource() {
    return source;
  }

  /**
   * Whether this request was resolved by the last resolution retry
   */
  private boolean isResolved() {
    return resolution != null || isDisposed;
  }

  /**
   * Decides whether the specified component could potentially resolve this
   * request.
   *
   * This is used as a hint to avoid unnecessarily retrying a resolution that
   * is not concerned with an event.
   *
   * TODO Currently we avoid forcing a resolution that will instantiate an
   * implementation we should specify the expected behavior.
   */
  public boolean isSatisfiedBy(Component candidate) {

    /*
     * Check if the candidate kind matches the target kind of the relation.
     * Consider the special case for instantiable implementations that can
     * satisfy an instance relation
     */
    boolean matchKind = relDef.getTargetKind().equals(candidate.getKind());
    /*
     * || (relation.getTargetKind().equals(ComponentKind.INSTANCE) &&
     * candidate.getKind().equals(ComponentKind.IMPLEMENTATION));
     */

    if (!matchKind) {
      return false;
    }

    /*
     * Check if the candidate matches the target of the relation
     */
    boolean matchTarget = false;

    if (relDef.getTarget() instanceof ComponentReference<?>) {
      Component target = CST.componentBroker.getComponent(relDef.getTarget().getName());
      matchTarget = (target != null) && (target.isAncestorOf(candidate) || target.equals(candidate));
    }

    if (relDef.getTarget() instanceof ResourceReference) {
      matchTarget = candidate.getProvidedResources().contains(relDef.getTarget());
    }

    if (!matchTarget) {
      return false;
    }

    /*
     * Check visibility
     */
    if (source instanceof Instance) {

      boolean promotion = false;
      for (RelationDefinition compoDep : ((Instance) source).getComposite().getCompType().getLocalRelations()) {
        if (relDef.matchRelation((Instance) source, compoDep)) {
          promotion = true;
        }
      }

      if (!promotion && !source.canSee(candidate))
        return false;

    } else if (!source.canSee(candidate)) {
      return false;
    }

    /*
     * Special validations for target instances
     */
    if (relDef.getTargetKind().equals(ComponentKind.INSTANCE)) {

      boolean valid = ((candidate instanceof Instance) && ((Instance) candidate).isSharable());
      /*
       * || ( (candidate instanceof Implementation) && ((Implementation)
       * candidate).isInstantiable());
       */

      if (!valid) {
        return false;
      }

    }

    /*
     * If this request has blocked the creating thread we should retry the
     * resolution to unblock it.
     *
     * Otherwise we verify if this request has not been already resolved by
     * this candidate (possibly in another thread) to avoid unnecessary
     * resolves.
     */

    synchronized (this) {
      if (this.isBlocked) {
        return true;
      }
    }

    Set<Link> resolutions = ((ComponentImpl) source).getExistingLinks(relDef.getName());

    /*
     * For single-valued relations we just verify there is some resolution
     */
    if (!relDef.isMultiple()) {
      return resolutions.isEmpty();
    }

    /*
     * For multi-valued relations we check if the candidate is already a
     * resolution
     */

    /*
     * boolean instantiatedCandidate =
     * (relation.getTargetKind().equals(ComponentKind.INSTANCE) &&
     * candidate.getKind().equals(ComponentKind.IMPLEMENTATION));
     */
    for (Link resolution : resolutions) {

      /*
       * if (instantiatedCandidate && (resolution.getDestination()
       * instanceof Instance) &&
       * ((Instance)resolution.getDestination()).getImpl
       * ().equals(candidate) ) return false;
       */
      if (resolution.getDestination().equals(candidate)) {
        return false;
      }

    }

    return true;
  }

  /**
   * Tries to resolve the request and wakes up the blocked thread
   */
  public void resolve() {

    /*
     * avoid multiple concurrent resolutions
     */
    synchronized (this) {
      if (isResolving) {
        return;
      }
    }

    /*
     * try to resolve.
     *
     * IMPORTANT resolution is performed outside synchronization, as it may
     * block in case of deployment. Notice also that the result is
     * temporarily confined to the stack before notifying pending threads.
     */

    Resolved<?> resolverResult = null;

    try {
      beginResolve();
      resolverResult = resolver.resolveLink(source, relDef);
    } finally {
      endResolve(resolverResult);
    }
  }

}
TOP

Related Classes of fr.imag.adele.apam.impl.PendingRequest

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.