package fr.imag.adele.apam.apform.impl;
import org.apache.felix.ipojo.ConfigurationException;
import fr.imag.adele.apam.Component;
import fr.imag.adele.apam.Implementation;
import fr.imag.adele.apam.Instance;
import fr.imag.adele.apam.Specification;
import fr.imag.adele.apam.declarations.ComponentKind;
import fr.imag.adele.apam.declarations.RelationDeclaration;
import fr.imag.adele.apam.declarations.instrumentation.CallbackDeclaration;
/**
* This is the callback associated to the APAM relation lifecycle
*
* @author vega
*/
public class RelationCallback extends InstanceCallback<Component> {
private final RelationDeclaration relation;
private final RelationDeclaration.Event trigger;
public RelationCallback(ApamInstanceManager.Apform instance, RelationDeclaration relation, RelationDeclaration.Event trigger, CallbackDeclaration callback) throws ConfigurationException {
super(instance,callback.getMethodName());
this.relation = relation;
this.trigger = trigger;
/*
* We force reflection meta-data calculation in the constructor to signal errors as soon as
* possible. This however has a cost in terms of early class loading.
*
* For partially declared relations, we need to wait until the apform has been fully reified
* in APAM in order to have access to the complete relation declaration.
*/
if (relation.getTargetKind() != null) {
try {
searchMethod();
} catch (NoSuchMethodException e) {
throw new ConfigurationException("invalid method declaration in callback "+getMethod()+ " for relation "+relation.getIdentifier());
}
}
}
public boolean isTriggeredBy(String relationName, RelationDeclaration.Event trigger) {
return this.relation.getIdentifier().equals(relationName) && this.trigger.equals(trigger);
}
/**
* Relation callback allows injection of the service object, instead of the target component.
*
* NOTE notice that we do not actually validate that the service object matches the parameter
* type. If declaration are not correct this will throw a cast exception.
*/
@Override
protected Object cast(Component argument) {
return ((Instance)argument).getServiceObject();
}
@Override
protected boolean isExpectedParameter(Class<?> parameterType) {
ComponentKind targetKind = relation.getTargetKind();
/*
* If target kind is not directly specified in the declaration, wee ask APAM to perform
* the calculation.
*/
if (targetKind == null && instance.getApamComponent() != null) {
targetKind = instance.getApamComponent().getRelation(relation.getIdentifier()).getTargetKind();
}
if (targetKind == null)
return false;
/*
* Calculate the expected parameter type depending on the target kind
*
* TODO for instance targets we allow the parameter to be a service object, but we do not validate
* the parameter type
*/
boolean isServiceParameter = false;
Class<?> expectedType = Component.class;
switch (targetKind) {
case SPECIFICATION:
expectedType = Specification.class;
break;
case IMPLEMENTATION:
expectedType = Implementation.class;
break;
case INSTANCE:
expectedType = Instance.class;
isServiceParameter = true;
break;
case COMPONENT:
expectedType = Component.class;
break;
default:
break;
}
return parameterType.isAssignableFrom(expectedType) || isServiceParameter;
}
}