Package fr.imag.adele.apam.apform.impl

Source Code of fr.imag.adele.apam.apform.impl.ApamInstanceManager$Apform

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

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;

import org.apache.felix.ipojo.ComponentInstance;
import org.apache.felix.ipojo.ConfigurationException;
import org.apache.felix.ipojo.HandlerManager;
import org.apache.felix.ipojo.InstanceManager;
import org.apache.felix.ipojo.MethodInterceptor;
import org.apache.felix.ipojo.architecture.PropertyDescription;
import org.apache.felix.ipojo.handlers.configuration.ConfigurationHandlerDescription;
import org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceDescription;
import org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceHandlerDescription;
import org.apache.felix.ipojo.metadata.Element;
import org.apache.felix.ipojo.util.Logger;
import org.osgi.framework.BundleContext;

import fr.imag.adele.apam.Apam;
import fr.imag.adele.apam.ApamComponent;
import fr.imag.adele.apam.CST;
import fr.imag.adele.apam.Component;
import fr.imag.adele.apam.Instance;
import fr.imag.adele.apam.Link;
import fr.imag.adele.apam.RelationDefinition;
import fr.imag.adele.apam.apform.Apform2Apam;
import fr.imag.adele.apam.apform.ApformInstance;
import fr.imag.adele.apam.apform.impl.handlers.PropertyInjectionHandler;
import fr.imag.adele.apam.apform.impl.handlers.RelationInjectionManager;
import fr.imag.adele.apam.declarations.AtomicImplementationDeclaration;
import fr.imag.adele.apam.declarations.InstanceDeclaration;
import fr.imag.adele.apam.declarations.RelationDeclaration;
import fr.imag.adele.apam.declarations.instrumentation.CallbackDeclaration;
import fr.imag.adele.apam.declarations.references.components.VersionedReference;
import fr.imag.adele.apam.impl.BaseApformComponent;
import fr.imag.adele.apam.impl.ComponentBrokerImpl;
import fr.imag.adele.apam.impl.ComponentImpl.InvalidConfiguration;
import fr.imag.adele.apam.impl.InstanceImpl;

public class ApamInstanceManager extends InstanceManager implements
    RelationInjectionManager.Resolver, MethodInterceptor {

  /**
   * The property used to configure this instance with its declaration
   */
  public final static String ATT_DECLARATION = "declaration";

  /**
   * Whether this instance was created directly using the APAM API
   */
  private final boolean isApamCreated;

  /**
   * The corresponding APAM declaration
   */
  private InstanceDeclaration declaration;

  /**
   * The associated Apform component
   */
  protected Apform apform;

  public ApamInstanceManager(ApamImplementationFactory implementation, boolean isApamCreated,
        BundleContext context, HandlerManager[] handlers) {

    super(implementation, context, handlers);

    this.isApamCreated = isApamCreated;
  }

  @Override
  public ApamImplementationFactory getFactory() {
    return (ApamImplementationFactory) super.getFactory();
  }

  @Override
  public Object getPojoObject() {
    if (getFactory().hasInstrumentedCode())
      return super.getPojoObject();

    return null;
  }

  public ApformInstance getApform() {
    return apform;
  }

  public Instance getApamComponent() {
    return getApform().getApamComponent();
  }

  @Override
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {

    String instanceName = (String) configuration.get("instance.name");

    declaration = (InstanceDeclaration) configuration.get(ApamInstanceManager.ATT_DECLARATION);
    if (isApamCreated || (declaration == null)) {
     
      declaration = new InstanceDeclaration(VersionedReference.any(getFactory().getApform().getDeclaration().getReference()), instanceName);
     
      for (Enumeration<String> properties = configuration.keys(); properties.hasMoreElements();) {
        String property = properties.nextElement();
        declaration.getProperties().put(property, configuration.get(property).toString());
      }
    }

    /*
     * Create the associated Apform component
     *
     * IMPORTANT This must be done before invoking super.configure() because APAM handler configuration
     * requires that the Apform be initialized.
     *
     * TODO refactor responsibilities between the Apform and the instance manager to avoid such fragile
     * ordering problems
     */
    apform = this.new Apform();

    /*
     * InstanceManager.configure assumes that there is instrumented code manipulation data, so we cannot
     * reuse this method for APAM abstract components. This is one of the drawbacks of trying to reuse
     * InstanceManager for all kind of APAM components.
     */

    if (getFactory().hasInstrumentedCode()) {
      configuration.put("instance.name", declaration.getName());
      super.configure(metadata, configuration);
    } else {
      // Add the name
      m_name = (String) configuration.get("instance.name");

      // Create the standard handlers and add these handlers to the list
      for (int i = 0; i < m_handlers.length; i++) {
        m_handlers[i].init(this, metadata, configuration);
      }
    }

    /*
     * TODO Currently there is no life-cycle handler, so we perform
     * configuration directly in the instance manager. Consider implementing
     * life-cycle management as a handler..
     */
    loadLifeCycleCallbacks();
  }

  /**
   * Notify instance activation/deactivation
   */
  @Override
  public void setState(int state) {
    super.setState(state);

    /*
     * Copy ipojo properties to declaration on validation
     */
    if (state == ComponentInstance.VALID) {
      ConfigurationHandlerDescription configuration = (ConfigurationHandlerDescription) getInstanceDescription()
          .getHandlerDescription("org.apache.felix.ipojo:properties");
      ProvidedServiceHandlerDescription provides = (ProvidedServiceHandlerDescription) getInstanceDescription()
          .getHandlerDescription("org.apache.felix.ipojo:provides");

      if (configuration != null) {
        for (PropertyDescription configurationProperty : configuration.getProperties()) {
          declaration.getProperties().put(configurationProperty.getName(),configurationProperty.getValue());
        }
      }

      if (provides != null) {
        for (ProvidedServiceDescription providedServiceDescription : provides.getProvidedServices()) {
          for (Object key : providedServiceDescription.getProperties().keySet()) {
            declaration.getProperties().put((String) key,providedServiceDescription.getProperties().get(key).toString());
          }
        }
      }
    }

    /*
     * For instances that are not created using the Apam API, register instance with APAM on validation
     */
    if (state == ComponentInstance.VALID && !isApamCreated)
      Apform2Apam.newInstance(getApform());

    if (state == ComponentInstance.INVALID)
      ((ComponentBrokerImpl)CST.componentBroker).disappearedComponent(getInstanceName());
  }

  /**
   * Delegate APAM to resolve a given injection.
   *
   * NOTE nothing is returned from this method, the call to APAM has as
   * side-effect the update of the relation.
   *
   * @param injection
   */
  @Override
  public boolean resolve(RelationInjectionManager injection) {

    /*
     * This instance is not actually yet managed by APAM
     */
    if (getApform().getApamComponent() == null) {
      System.err.println("resolve failure for client " + getInstanceName() + " : APAM instance unkown");
      return false;
    }

    Apam apam = getFactory().getApam();
    if (apam == null) {
      System.err.println("resolve failure for client " + getInstanceName() + " : APAM not found");
      return false;
    }

    /*
     * Find the relation to resolve and trigger resolution at the level
     * specified in the source kind
     */

    RelationDefinition relDef = getApform().getApamComponent().getRelation(injection.getRelation().getIdentifier());

    Component source = null;
    switch (relDef.getSourceKind()) {
    case INSTANCE:
    case COMPONENT:
      source = getApform().getApamComponent();
      break;
    case IMPLEMENTATION:
      source = getApform().getApamComponent().getImpl();
      break;
    case SPECIFICATION:
      source = getApform().getApamComponent().getSpec();
      break;
    }

    return CST.apamResolver.resolveLink(source, relDef) != null;

  }

  /**
   * Delegate APAM to remove the currently resolved relation and force a new
   * resolution the next time the injected relation is accessed
   *
   */
  @Override
  public boolean unresolve(RelationInjectionManager injection) {

    /*
     * This instance is not actually yet managed by APAM
     */
    if (getApform().getApamComponent() == null) {
      // System.err.println("unresolve failure for client " +
      // getInstanceName() + " : ASM instance unkown");
      return false;
    }

    Apam apam = getFactory().getApam();
    if (apam == null) {
      System.err.println("unresolve failure for client " + getInstanceName() + " : APAM not found");
      return false;
    }

    RelationDeclaration relation = injection.getRelation();
    for (Link outgoing : ((InstanceImpl)getApform().getApamComponent()).getExistingLinks(relation.getIdentifier())) {
      outgoing.remove();
    }

    return true;
  }

  /**
   * Adds a new injected field to this instance
   */
  @Override
  public void addInjection(RelationInjectionManager relation) {
    apform.injectedFields.add(relation);
  }

  /**
   * Get the list of injected fields
   */
  public Set<RelationInjectionManager> getInjections() {
    return apform.injectedFields;
  }

  /**
   * Loads the definition of the life-cycle callbacks associated with this
   * instance
   */
  private void loadLifeCycleCallbacks() throws ConfigurationException {

    if (!(getFactory().getDeclaration() instanceof AtomicImplementationDeclaration))
      return;

    AtomicImplementationDeclaration implementation = (AtomicImplementationDeclaration) getFactory().getDeclaration();

    /*
     * Add automatically a life-cycle callback in case the class implements directly the APAM component interface
     */
    Class<?> pojoClass = this.getClazz();
    if (ApamComponent.class.isAssignableFrom(pojoClass)) {
      implementation.addCallback(AtomicImplementationDeclaration.Event.INIT,    new CallbackDeclaration(implementation, "apamInit"));
      implementation.addCallback(AtomicImplementationDeclaration.Event.REMOVE, new CallbackDeclaration(implementation, "apamRemove"));
    }
   
    /*
     * register callbacks
     */
    for (AtomicImplementationDeclaration.Event trigger : AtomicImplementationDeclaration.Event.values()) {

      Set<CallbackDeclaration> callbacks = implementation.getCallback(trigger);

      if (callbacks == null)
        continue;

      for (CallbackDeclaration callbackDeclaration : callbacks) {
       
        LifecycleCallback callback = new LifecycleCallback(this, trigger, callbackDeclaration);
        addCallback(callback);
       
        /*
         * track remove callback execution
         */
        if (trigger == AtomicImplementationDeclaration.Event.REMOVE) {
          register(callback.getMethodMetadata(),this);
        }
      }

    }
  }


  /**
   * Stop the component when the remove callback is explicitly invoked by code
   */
  @Override
  public void onFinally(Object pojo, Member method) {
   
    if (getApform().getApamComponent() != null) {
     
      /*
       * avoid re-executing the invoked callback
       */
      LifecycleCallback triggering = null;
      for (LifecycleCallback callback : this.apform.lifeCycleCallbacks) {
        if (callback.invokes((Method)method))
          triggering = callback;
      }
     
      if (triggering != null)
        removeCallback(triggering);
     
      /*
       * stop the instance
       */
      stop();
    }
  }

  @Override
  public void onEntry(Object pojo, Member method, Object[] args) {
  }

  @Override
  public void onExit(Object pojo, Member method, Object returnedObj) {
  }

  @Override
  public void onError(Object pojo, Member method, Throwable throwable) {
  }

  /**
   * Adds a new life-cycle change callback
   */
  public void addCallback(LifecycleCallback callbackthrows ConfigurationException {
    apform.lifeCycleCallbacks.add(callback);
  }

  private void removeCallback(LifecycleCallback callback)  {
    apform.lifeCycleCallbacks.remove(callback);
  }

  /**
   * Adds a new property change callback
   */
  public void addCallback(PropertyCallback callback) throws ConfigurationException {
    apform.propertyCallbacks.add(callback);
  }

  /**
   * Adds a new relation life-cycle callback
   */
  public void addCallback(RelationCallback callback) throws ConfigurationException {
    apform.relationCallbacks.add(callback);
  }

  /**
   * This class represents the mediator between APAM and this instance manager
   */
  public class Apform extends  BaseApformComponent<Instance, InstanceDeclaration> implements ApformInstance {

    /**
     * The list of injected fields handled by this instance
     */
    private final Set<RelationInjectionManager> injectedFields;

    /**
     * The list of callbacks to notify when onInit, onRemove
     */
    private final Set<LifecycleCallback> lifeCycleCallbacks;

    /**
     * The list of callbacks to notify when a property is set
     */
    private final Set<PropertyCallback> propertyCallbacks;

    /**
     * The list of callbacks to notify when bind, Unbind
     */
    private final Set<RelationCallback> relationCallbacks;

    private Apform() throws ConfigurationException {

      super(ApamInstanceManager.this.declaration);

      injectedFields = new HashSet<RelationInjectionManager>();
      lifeCycleCallbacks = new HashSet<LifecycleCallback>();
      propertyCallbacks = new HashSet<PropertyCallback>();
      relationCallbacks = new HashSet<RelationCallback>();

    }

    public InstanceManager getManager() {
      return ApamInstanceManager.this;
    }

    @Override
    public Object getServiceObject() {
      return ApamInstanceManager.this.getPojoObject();
    }

    @Override
    public void setApamComponent(Component apamComponent) throws InvalidConfiguration {

      if (this.apamComponent == apamComponent)
        return;
     
      Instance previousComponent = this.apamComponent;
      super.setApamComponent(apamComponent);

      /*
       * Invoke the execution platform instance callback on the pojo
       * object
       */

      Object pojo = getServiceObject();
      if (pojo == null)
        return;

      PropertyInjectionHandler handler = (PropertyInjectionHandler) ApamInstanceManager.this.getHandler(
                          ApamComponentFactory.APAM_NAMESPACE + ":"+ PropertyInjectionHandler.NAME);

      if (apamComponent != null) { // starting the instance

        if (handler != null)
          handler.setApamComponent(apamComponent);

        fireCallbacks(AtomicImplementationDeclaration.Event.INIT,this.apamComponent,false);
        return;
      }

      if (apamComponent == null) { // stopping the instance

        fireCallbacks(AtomicImplementationDeclaration.Event.REMOVE,previousComponent,true);

        /*
         * dispose this instance
         */
        if (handler != null)
          handler.setApamComponent(null);

        ApamInstanceManager.this.dispose();

        return;
      }

    }

    @Override
    public boolean checkLink(Component destination, String depName) {

      /*
       * Validate all the instrumentations can be performed
       */

      for (RelationInjectionManager injectedField : injectedFields) {
        if (!injectedField.isValid())
          return false;
      }

      return true;
    }

    @Override
    public boolean setLink(Component destination, String depName) {

      /*
       * perform injection update
       */
      for (RelationInjectionManager injectedField : injectedFields) {
        if (injectedField.getRelation().getIdentifier().equals(depName)) {
          injectedField.addTarget(destination);
        }
      }

      /*
       * perform callback bind
       */

      Object pojo = getServiceObject();
      if (pojo == null)
        return true;

      fireCallbacks(RelationDeclaration.Event.BIND, depName, destination);

      return true;
    }

    @Override
    public boolean remLink(Component destination, String depName) {


      /*
       * perform injection update
       */
      for (RelationInjectionManager injectedField : injectedFields) {
        if (injectedField.getRelation().getIdentifier().equals(depName)) {
          injectedField.removeTarget(destination);
        }
      }

      /*
       * perform callback unbind
       */

      Object pojo = getServiceObject();
      if (pojo == null)
        return true;

      fireCallbacks(RelationDeclaration.Event.UNBIND, depName,destination);

      return true;
    }

    @Override
    public void setProperty(String attr, String value) {

      Object pojo = getPojoObject();
      if (pojo == null)
        return;

      fireCallbacks(attr, super.getApamComponent().getPropertyObject(attr));
    }

    private void fireCallbacks(AtomicImplementationDeclaration.Event event, Instance component, boolean ignoreException) throws InvalidConfiguration {
      for (LifecycleCallback callback : lifeCycleCallbacks) {
        try {
          if (callback.isTriggeredBy(event))
            callback.invoke(component);
        } catch (Throwable exception) {

          if (exception instanceof InvocationTargetException) {
            exception = exception.getCause();
          }
         
          getLogger().log(Logger.ERROR,"error invoking lifecycle callback "+ callback.getMethod(), exception);
          if (! ignoreException)
            throw new InvalidConfiguration(exception);
        }
      }
     

    }

    private void fireCallbacks(String attr, Object value)   {
      for (PropertyCallback callback : propertyCallbacks) {
        try {
          if (callback.isTriggeredBy(attr))
            callback.invoke(value);
        } catch (Exception ignored) {
          getLogger().log(Logger.ERROR,"error invoking callback " + callback.getMethod() + " for property " + attr, ignored);
        }
      }
    }

    private void fireCallbacks(RelationDeclaration.Event event,  String relationName, Component target)  {
      for (RelationCallback callback : relationCallbacks) {
        try {
          if (callback.isTriggeredBy(relationName, event))
            callback.invoke(target);
        } catch (Exception ignored) {
          getLogger().log(Logger.ERROR,"error invoking relation callback " + callback.getMethod() + " for relation" + relationName, ignored);
        }
      }
    }

  }

}
TOP

Related Classes of fr.imag.adele.apam.apform.impl.ApamInstanceManager$Apform

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.