/**
* 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.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.imag.adele.apam.Apam;
import fr.imag.adele.apam.ApamManagers;
import fr.imag.adele.apam.CST;
import fr.imag.adele.apam.Component;
import fr.imag.adele.apam.Composite;
import fr.imag.adele.apam.DynamicManager;
import fr.imag.adele.apam.Instance;
import fr.imag.adele.apam.Link;
import fr.imag.adele.apam.PropertyManager;
import fr.imag.adele.apam.RelationDefinition;
import fr.imag.adele.apam.declarations.CreationPolicy;
import fr.imag.adele.apam.declarations.InstanceDeclaration;
import fr.imag.adele.apam.impl.ComponentImpl.InvalidConfiguration;
/**
* This class implements the manager in charge of dynamically updating the
* architecture, in response to execution context evolution.
*
* @author vega
*
*/
public class DynaMan implements DynamicManager, PropertyManager {
private final static Logger logger = LoggerFactory.getLogger(DynaMan.class);
/**
* A reference to the APAM machine
*/
@SuppressWarnings("unused")
private Apam apam;
/**
* The list of contained instances that must be dynamically created when the
* specified triggering condition is satisfied
*/
private final List<FutureInstance> futureInstances;
/**
* The list of dynamic dependencies that must be updated without waiting for
* lazy resolution
*/
private final List<PendingRequest> dynamicDependencies;
/**
* The task executor. We use a pool of a threads to handle resolutions and instantiations
* that may block
*/
private final Executor taskExecutor = Executors.newCachedThreadPool(new ThreadPoolFactory("dynaman", Executors.defaultThreadFactory()));
/**
* The task in charge of performing dynamic resolution
*/
private class ResolutionTask implements Runnable {
private final PendingRequest request;
private final Component candidate;
public ResolutionTask(PendingRequest request, Component candidate) {
this.request = request;
this.candidate = candidate;
}
@Override
public void run() {
/*
* perform resolution
*/
request.resolve();
/*
* If the relation is marked as dynamic/eager and optional, we simply ignore unsuccessful
* asynchronous resolutions.
*/
if (request.getResolution() == null) {
return;
}
/*
* If the target is a non sharable instance, and the resolution was successful, we reduce
* the priority of the request to avoid starvation of other dynamic request expecting the
* same target
*/
if (candidate != null && candidate instanceof Instance && request.getResolution() != null) {
if (!((Instance)candidate).isSharable())
reducePriorityDynamicRequest(request);
}
}
}
/**
* The task in charge of instantiating declared future instances
*/
private class InstantiationTask implements Runnable {
private final FutureInstance instantiableFutureInstance;
public InstantiationTask(FutureInstance instantiableFutureInstance) {
this.instantiableFutureInstance = instantiableFutureInstance;
}
@Override
public void run() {
/*
* Try to instantiate triggered instance
*
*/
instantiableFutureInstance.instantiate();
/*
* Put the request back in the pending list, if it did not succeed
*/
if (!instantiableFutureInstance.isInstantiated()) {
addFutureInstance(instantiableFutureInstance);
}
}
}
/**
* The dynamic manager handle all request concerned with dynamic management
* of links
*/
public DynaMan() {
dynamicDependencies = new ArrayList<PendingRequest>();
futureInstances = new ArrayList<FutureInstance>();
}
/**
* Dynamic manager identifier
*/
@Override
public String getName() {
return CST.DYNAMAN;
}
/**
* Schedule resolution of all the requests that are potentially satisfied by a given component
*/
private void resolveDynamicRequests(Component candidate) {
for (PendingRequest request : getDynamicRequests()) {
if (request.isSatisfiedBy(candidate)) {
taskExecutor.execute(new ResolutionTask(request, candidate));
}
}
}
/**
* Add a new dynamic request
*/
private void addDynamicRequest(PendingRequest request) {
synchronized (dynamicDependencies) {
dynamicDependencies.add(request);
}
}
/**
* Remove a dynamic request
*/
private void removeDynamicRequest(PendingRequest request) {
synchronized (dynamicDependencies) {
dynamicDependencies.remove(request);
}
}
/**
* Reduce the priority of a request
*/
private void reducePriorityDynamicRequest(PendingRequest request) {
synchronized (dynamicDependencies) {
if (dynamicDependencies.remove(request))
dynamicDependencies.add(request);
}
}
/**
* Updates the list of dynamic dependencies when a new component is managed
*/
private void addDynamicRequests(Component component) {
for (RelationDefinition relDef : component.getRelations()) {
if (component.getKind().equals(relDef.getSourceKind()) && relDef.isDynamic()) {
PendingRequest request = new PendingRequest(CST.apamResolver, component, relDef);
addDynamicRequest(request);
/*
* In casez of eager relationships, schedule initial resolution
*/
if (relDef.getCreation() == CreationPolicy.EAGER) {
taskExecutor.execute(new ResolutionTask(request, null));
}
}
}
}
/**
* Get a thread-safe (stack confined) copy of the dynamic requests
*/
private List<PendingRequest> getDynamicRequests() {
synchronized (dynamicDependencies) {
return new ArrayList<PendingRequest>(dynamicDependencies);
}
}
/**
* Add a new future Instance
*/
private void addFutureInstance(FutureInstance request) {
synchronized (futureInstances) {
futureInstances.add(request);
}
}
/**
* Remove a dynamic request
*/
private void removeFutureInstance(FutureInstance request) {
synchronized (futureInstances) {
futureInstances.remove(request);
}
}
/**
* Verifies if the triggering conditions of pending future instances defined in the specified
* composite are satisfied
*/
private void resolveFutureInstances(Composite context) {
/*
* Take a snapshot of future instances that can be instantiated in the updated context
*
* IMPORTANT Notice that we remove the request from the list of future instances, so that we
* are sure* that there is a single thread handling a given pending instantiation request
*
*/
List<FutureInstance> instantiableFutureInstances = new ArrayList<FutureInstance>();
synchronized (futureInstances) {
for (FutureInstance futureInstance : futureInstances) {
if (futureInstance.getOwner().equals(context) && futureInstance.isInstantiable()) {
instantiableFutureInstances.add(futureInstance);
}
}
futureInstances.removeAll(instantiableFutureInstances);
}
/*
* Schedule instantiation of triggered instances
*
*/
for (FutureInstance instantiableFutureInstance : instantiableFutureInstances) {
taskExecutor.execute(new InstantiationTask(instantiableFutureInstance));
}
}
/**
* Updates the list of future instances when a new composite is managed
*/
private void addFutureInstances(Composite composite) {
/*
* Create the future instance corresponding to the declared start sentences
*/
for (InstanceDeclaration instanceDeclaration : composite.getCompType().getCompoDeclaration().getInstanceDeclarations()) {
try {
addFutureInstance( new FutureInstance(composite, instanceDeclaration));
} catch (InvalidConfiguration error) {
logger.error("Error managing dynamic instances for composite " + composite.getName(), error);
}
}
/*
* Evaluate the the triggering conditions in the initial configuration
*/
resolveFutureInstances(composite);
}
/**
* Get a thread-safe (stack confined) copy of the dynamic requests
*/
private List<FutureInstance> getFutureInstances() {
synchronized (futureInstances) {
return new ArrayList<FutureInstance>(futureInstances);
}
}
/**
* Reevaluate all dynamic relationships and instantiations impacted by changes in the
* specified component
*/
private void propagateComponentChange(Component component) {
resolveDynamicRequests(component);
if (component instanceof Instance) {
resolveFutureInstances(((Instance)component).getComposite());
}
}
@Override
public void addedComponent(Component component) {
/*
* Add the dynamic dependencies and future instances of the newly-added
* component
*/
addDynamicRequests(component);
if (component instanceof Composite) {
addFutureInstances((Composite) component);
}
/*
* Verify if the new component satisfies some pending requests or the
* triggering condition of future instances
*/
propagateComponentChange(component);
}
@Override
public void addedLink(Link link) {
}
@Override
public void attributeAdded(Component component, String attr, String newValue) {
propertyChanged(component, attr);
}
@Override
public void attributeChanged(Component component, String attr, String newValue, String oldValue) {
propertyChanged(component, attr);
}
@Override
public void attributeRemoved(Component component, String attr, String oldValue) {
propertyChanged(component, attr);
}
private void propertyChanged(Component component, String property) {
/*
* Verify if the updated component satisfies some pending requests or
* the triggering condition of future instances
*/
propagateComponentChange(component);
}
public void ownershipChanged(Instance instance) {
/*
* Verify if the component in the new context satisfies some pending
* requests or the triggering condition of future instances
*/
propagateComponentChange(instance);
}
@Override
public void removedComponent(Component component) {
/*
* Remove from the list of dynamic requests all requests originating
* from the removed component
*/
for (PendingRequest request : getDynamicRequests()) {
if (request.getSource().equals(component)) {
removeDynamicRequest(request);
}
}
/*
* Remove from the list of future instances all requests originating
* from the removed component
*/
for (FutureInstance request : getFutureInstances()) {
if (request.getOwner().equals(component)) {
removeFutureInstance(request);
}
}
}
@Override
public void removedLink(Link link) {
/*
* If the target of the wire is a non sharable instance, the released
* instance can potentially be used by a pending requests.
*/
if (link.getDestination() instanceof Instance) {
Instance candidate = (Instance) link.getDestination();
if ((!candidate.isShared()) && candidate.isSharable()) {
resolveDynamicRequests(candidate);
}
}
}
/**
* This method is automatically invoked when the manager is validated, so we
* can safely assume that APAM is available
*/
public synchronized void start(Apam apam) {
this.apam = apam;
ApamManagers.addDynamicManager(this);
ApamManagers.addPropertyManager(this);
}
/**
* This method is automatically invoked when the manager is invalidated, so
* APAM is no longer available
*/
public synchronized void stop() {
ApamManagers.removeDynamicManager(this);
ApamManagers.removePropertyManager(this);
/*
* Try to dispose all dynamic requests
*/
for (PendingRequest request : getDynamicRequests()) {
request.dispose();
}
this.apam = null;
}
}