/** * 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.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import fr.imag.adele.apam.ApamManagers; import fr.imag.adele.apam.AttrType; import fr.imag.adele.apam.CST; import fr.imag.adele.apam.Component; import fr.imag.adele.apam.Composite; import fr.imag.adele.apam.CompositeType; import fr.imag.adele.apam.DynamicManager; import fr.imag.adele.apam.Implementation; import fr.imag.adele.apam.Instance; import fr.imag.adele.apam.Link; import fr.imag.adele.apam.RelToResolve; import fr.imag.adele.apam.RelationDefinition; import fr.imag.adele.apam.Specification; import fr.imag.adele.apam.apform.ApformComponent; import fr.imag.adele.apam.declarations.ComponentDeclaration; import fr.imag.adele.apam.declarations.InjectedPropertyPolicy; import fr.imag.adele.apam.declarations.PropertyDefinition; import fr.imag.adele.apam.declarations.RelationDeclaration; import fr.imag.adele.apam.declarations.references.resources.ResourceReference; import fr.imag.adele.apam.util.ApamFilter; import fr.imag.adele.apam.util.Attribute; import fr.imag.adele.apam.util.Substitute; import fr.imag.adele.apam.util.Util; import fr.imag.adele.apam.util.Visible; public abstract class ComponentImpl extends ConcurrentHashMap<String, Object> implements Component, Comparable<Component> { /** * An exception that can be thrown in the case of problems while creating a * component */ public static class InvalidConfiguration extends Exception { private static final long serialVersionUID = 1L; public InvalidConfiguration(String message) { super(message); } public InvalidConfiguration(String message, Throwable cause) { super(message, cause); } public InvalidConfiguration(Throwable cause) { super(cause); } } private final Object componentId = new Object(); // only for hashCode private static final long serialVersionUID = 1L; protected static Logger logger = LoggerFactory.getLogger(ComponentImpl.class); private final ApformComponent apform; private final ComponentDeclaration declaration; /* * set to true if an instantiation failed. * True means : do not try to instantiate again */ private boolean instantiateFails = false ; // Contains the composite type that was the first to physically deploy that // component. private CompositeType firstDeployed; // Set of Dependencies private Map<String, RelationDefinition> relDef = new HashMap<String, RelationDefinition>(); protected final Set<Link> links = Collections.newSetFromMap(new ConcurrentHashMap<Link, Boolean>()); protected final Set<Link> invlinks = Collections.newSetFromMap(new ConcurrentHashMap<Link, Boolean>()); public ComponentImpl(ApformComponent apform) throws InvalidConfiguration { if (apform == null) { throw new InvalidConfiguration("Null apform instance while creating component"); } this.apform = apform; this.declaration = apform.getDeclaration(); } /** * Whether the component is instantiable */ @Override public boolean canSee(Component target) { return Visible.isVisible(this, target); } /** * TODO Assumes that all components are in the same name space, including * instance !! */ @Override public int compareTo(Component that) { return this.getName().compareTo(that.getName()); } @Override public boolean createLink(Component to, RelToResolve dep, boolean hasConstraints, boolean promotion) { // Not a relation : a find if (!dep.isRelation()) { return true; } if (CST.isFinalRelation(dep.getName())) { logger.error("CreateLink: cannot create predefined relation " + dep.getName()); return false; } if ((to == null) || (dep == null)) { logger.error("CreateLink: Source or target are null "); return false; } if (!promotion && !canSee(to)) { logger.error("CreateLink: Source " + this + " does not see its target " + to); return false; } if (this.getKind() != dep.getSourceKind()) { logger.error("CreateLink: Source kind " + getKind() + " is not compatible with relation sourceType " + dep.getSourceKind()); return false; } if (to.getKind() != dep.getTargetKind()) { logger.error("CreateLink: Target kind " + to.getKind() + " is not compatible with relation targetType " + dep.getTargetKind()); return false; } if (hasConstraints && !dep.matchRelationConstraints(to)) { logger.error("CreateLink: Target does not satisfies the constraints" ); return false ; } String depName = dep.getName(); for (Link link : links) { // check if it already exists if ((link.getDestination() == to) && link.getName().equals(depName)) { // It exists, do nothing. return true; } } // creation if (!getApformComponent().checkLink(to, depName)) { logger.error("CreateLink: INTERNAL ERROR: link from " + this + " to " + to + " could not be created in the real instance."); return false; } Link link = new LinkImpl(this, to, dep, hasConstraints, promotion); links.add(link); ((ComponentImpl) to).invlinks.add(link); getApformComponent().setLink(to, depName); // Notify Dynamic managers that a new link has been created for (DynamicManager manager : ApamManagers.getDynamicManagers()) { manager.addedLink(link); } return true; } /** * Components are uniquely represented in the Apam state model, so we use * reference equality in all comparisons. */ @Override public final boolean equals(Object o) { return (this == o); } /** * To be called when the object is fully loaded and chained. Terminate the * generic initialization : computing the dependencies, and the properties * * @param initialProperties */ public void finishInitialize(Map<String, String> initialProperties) { initializeRelations(); initializeProperties(initialProperties); } /** * Get the value of all the properties in the component. * */ @Override public Map<String, Object> getAllProperties() { return Collections.unmodifiableMap(this); } @Override public Map<String, String> getAllPropertiesString() { Map<String, String> ret = new HashMap<String, String>(); for (Entry<String, Object> e : this.entrySet()) { if (!Attribute.isFinalAttribute(e.getKey())) { ret.put(e.getKey(), Util.toStringAttrValue(e.getValue())); } } return ret; } @Override public final ApformComponent getApformComponent() { return apform; } /** * Tries to find the definition of a property, the property can be valued in this component * or in its defining group * * @param attribute * @return */ @Override public PropertyDefinition getPropertyDefinition(String attribute) { return getAttrDefinition(attribute); } /** * Tries to find the definition of attribute "attr" associated with * component "component". Returns null if the attribute is not explicitly * defined * * @param component * @param attr * @return */ public PropertyDefinition getAttrDefinition(String attr) { // PropertyDefinition definition = // getDeclaration().getPropertyDefinition(attr); // if (definition != null) { // return definition; // } PropertyDefinition definition = null; Component group = this; // .getGroup(); while (group != null) { definition = group.getDeclaration().getPropertyDefinition(attr); if (definition != null) { return definition; } group = group.getGroup(); } return null; } @Override public AttrType getAttrType(String attr) { PropertyDefinition attrDef = getAttrDefinition(attr); if (attrDef == null) { if (Attribute.isFinalAttribute(attr)) { return Attribute.splitType("string"); } return null; } return Attribute.splitType(attrDef.getType()); } @Override public final ComponentDeclaration getDeclaration() { return declaration; } /** * Return the first link having this name, on any ancestor, but do not try * to resolve is none are found * * @param relName * @return */ public Link getExistingLink(String relName) { Component group = this; while (group != null) { for (Link link : ((ComponentImpl) group).getLocalLinks()) { if (link.getName().equals(relName)) { return link; } } group = group.getGroup(); } return null; } /** * Return all the link of that names, but do not resolve if nore are found * * @param relName * @return */ public Set<Link> getExistingLinks(String relName) { Set<Link> dests = new HashSet<Link>(); Component group = this; while (group != null) { for (Link link : ((ComponentImpl) group).getLocalLinks()) { if (link.getName().equals(relName)) { dests.add(link); } } group = group.getGroup(); } return dests; } private Set<Component> getFInalLinkDests(String depName) { Set<Component> dests = new HashSet<Component>(); // if (depName.equals(CST.REL_GROUP)) { // dests.add(getGroup()) ; // return dests ; // } // if (depName.equals(CST.REL_MEMBERS)) { // dests.addAll(getMembers()) ; // return dests ; // } if (depName.equals(CST.REL_COMPOSITE)) { if (this instanceof Instance) { dests.add(((Instance) this).getComposite()); } return dests; } if (depName.equals(CST.REL_COMPOTYPE)) { if (this instanceof Instance) { dests.add(((Instance) this).getComposite().getCompType()); } else if (this instanceof Implementation) { dests.addAll(((Implementation) this).getInCompositeType()); } return dests; } if (depName.equals(CST.REL_CONTAINS)) { if (this instanceof Composite) { dests.addAll(((Composite) this).getContainInsts()); } else if (this instanceof CompositeType) { dests.addAll(((CompositeType) this).getImpls()); } return dests; } if (depName.equals(CST.REL_SPEC)) { if (this instanceof Specification) { dests.add(this); } else if (this instanceof Implementation) { dests.add(((Implementation) this).getSpec()); } else if (this instanceof Instance) { dests.add(((Instance) this).getSpec()); } return dests; } if (depName.equals(CST.REL_IMPL)) { if (this instanceof Implementation) { dests.add(this); } else if (this instanceof Instance) { dests.add(((Instance) this).getImpl()); } return dests; } if (depName.equals(CST.REL_INST)) { if (this instanceof Instance) { dests.add(this); } return dests; } if (depName.equals(CST.REL_IMPLS)) { if (this instanceof Specification) { dests.addAll(this.getMembers()); } else if (this instanceof Implementation) { dests.addAll(((Implementation) this).getSpec().getMembers()); } else { dests.addAll(((Instance) this).getSpec().getMembers()); } return dests; } if (depName.equals(CST.REL_INSTS)) { if (this instanceof Implementation) { dests.addAll(this.getMembers()); } else if (this instanceof Instance) { dests.addAll(((Instance) this).getImpl().getMembers()); } return dests; } if (depName.equals(CST.REL_INST)) { if (this instanceof Instance) { dests.add(this); } return dests; } return dests; } @Override public CompositeType getFirstDeployed() { return firstDeployed == null ? CompositeTypeImpl.getRootCompositeType() : firstDeployed; } @Override public Link getInvLink(Component destInst) { if (destInst == null) { return null; } for (Link link : invlinks) { if (link.getDestination() == destInst) { return link; } } return null; } @Override public Link getInvLink(Component destInst, String depName) { if (destInst == null) { return null; } for (Link link : invlinks) { if ((link.getDestination() == destInst) && (link.getName().equals(depName))) { return link; } } return null; } @Override public Set<Link> getInvLinks() { return Collections.unmodifiableSet(invlinks); } @Override public Set<Link> getInvLinks(Component destInst) { if (destInst == null) { return null; } Set<Link> w = new HashSet<Link>(); for (Link link : invlinks) { if (link.getDestination() == destInst) { w.add(link); } } return w; } @Override public Set<Link> getInvLinks(String depName) { Set<Link> w = new HashSet<Link>(); for (Link link : invlinks) { if ((link.getDestination() == this) && (link.getName().equals(depName))) { w.add(link); } } return w; } @Override public Link getLink(String relName) { Component group = this; while (group != null) { for (Link link : ((ComponentImpl) group).getLocalLinks()) { if (link.getName().equals(relName)) { return link; } } group = group.getGroup(); } // None are present. Try to resolve RelationDefinition rel = getRelation(relName); if (rel == null) { logger.error("relation " + relName + " undefined for " + this); return null; } Component source = rel.getRelSource(this); CST.apamResolver.resolveLink(source, rel); return getExistingLink(relName); } /** * resolve */ @Override public Component getLinkDest(String depName) { Link link = getLink(depName); return (link == null) ? null : link.getDestination(); } // =================================== Links ========= /** * returns the connections towards the service instances actually used. * return only APAM links. */ @Override public Set<Component> getLinkDests(String depName) { if (depName == null || depName.isEmpty()) { return null; } if (CST.isFinalRelation(depName)) { return getFInalLinkDests(depName); } Set<Component> dests = new HashSet<Component>(); for (Link link : getLinks(depName)) { if (link.getName().equals(depName)) { dests.add(link.getDestination()); } } return dests; } /** * Inherits and resolve */ @Override public Set<Link> getLinks(String relName) { Set<Link> dests = getExistingLinks(relName); if (!dests.isEmpty()) { return dests; } // None are present. Try to resolve RelationDefinition rel = getRelation(relName); if (rel == null) { logger.error("relation " + relName + " undefined for " + this); return null; } // if (!rel.isLazy()) // return dests ; Component source = rel.getRelSource(this); CST.apamResolver.resolveLink(source, rel); return getExistingLinks(relName); } /** * Only return the links instantiated on that source component (no * inheritance, no resolution) */ public Set<Link> getLocalLinks() { return Collections.unmodifiableSet(links); } protected RelationDefinition getLocalRelation(String id) { return relDef.get(id); } @Override public Collection<RelationDefinition> getLocalRelations() { return Collections.unmodifiableCollection(relDef.values()); } @Override public final String getName() { return declaration.getName(); } /** * Get the value of the property. * * Attributes are supposed to be correct and inherited statically * */ @Override public String getProperty(String attr) { return Util.toStringAttrValue(getPropertyObject(attr)); } /** * Get the value of a property, the property can be valued in this component * or in its defining group Return will be an object of type int, String, * boolean for attributes declared int, String, boolean String for an * enumeration. * * For sets, the return will be an array of the corresponding types. i.e; * int[], String[] and so on. Returns null if attribute is not defined or * not set. */ @Override public Object getPropertyObject(String attribute) { Object value = get(attribute); if (value == null) { return null; // all attributes, including default values are already there // if(value==null && getDeclaration()!=null) // value=getDeclaration().getProperty(attribute); } return Substitute.substitute(attribute, value, this); } @Override public Set<ResourceReference> getProvidedResources() { Set<ResourceReference> provided = new HashSet<ResourceReference>(); /* * add all provided resources declared at all levels of abstraction */ Component ancestor = this; while (ancestor != null) { provided.addAll(ancestor.getDeclaration().getProvidedResources()); ancestor = ancestor.getGroup(); } return Collections.unmodifiableSet(provided); } // Set<ResourceReference> allResources = new HashSet<ResourceReference> () ; // Component current = this ; // while (current != null) { // if (current.getDeclaration().getProvidedResources() != null) // allResources.addAll (current.getDeclaration().getProvidedResources()) ; // current = current.getGroup() ; // } // return allResources ; // } /** * WARNING : no resolution */ @Override public Set<Component> getRawLinkDests() { Set<Component> dests = new HashSet<Component>(); for (Link link : getRawLinks()) { dests.add(link.getDestination()); } return dests; } /** * No resolution * * @param depName * @return */ public Set<Component> getRawLinkDests(String depName) { if (depName == null || depName.isEmpty()) { return null; } if (CST.isFinalRelation(depName)) { return getFInalLinkDests(depName); } Set<Component> dests = new HashSet<Component>(); for (Link link : getRawLinks()) { if (link.getName().equals(depName)) { dests.add(link.getDestination()); } } return dests; } /** * Warning : do not resolve ! */ @Override public Set<Link> getRawLinks() { Set<Link> allLinks = new HashSet<Link>(); Component group = this; while (group != null) { allLinks.addAll(((ComponentImpl) group).getLocalLinks()); group = group.getGroup(); } return allLinks; } /** * Return the relation with name "id" if it can be applied to this * component. * * A relation D can be applied on a component source if D.Id == id D.source * must be the name of source or of an ancestor of source, and D.SourceKind * == source.getKind. * * Looks in the group, and then in the composite type, if source in an * instance in all composite types if source is an implem. * * @param source * @param id * @return */ @Override public RelationDefinition getRelation(String id) { RelationDefinition dep = null; Component group = this; while (group != null) { dep = ((ComponentImpl) group).getLocalRelation(id); if (dep != null) { return dep; } group = group.getGroup(); } // Looking for composite definitions. if (this instanceof Instance) { CompositeType comptype = ((Instance) this).getComposite() .getCompType(); dep = comptype.getCtxtRelation(this, id); if (dep != null) { return dep; } } if (this instanceof Implementation) { for (CompositeType comptype : ((Implementation) this) .getInCompositeType()) { dep = comptype.getCtxtRelation(this, id); if (dep != null) { return dep; } } } return null; } @Override public Set<RelationDefinition> getRelations() { Set<RelationDefinition> relDefs = new HashSet<RelationDefinition>(); Set<String> processed = new HashSet<String>(); Component group = this; while (group != null) { for (RelationDefinition relDef : group.getLocalRelations()) { if (!processed.contains(relDef.getName())) { relDefs.add(relDef); processed.add(relDef.getName()); } } group = group.getGroup(); } // Looking for composite definitions. if (this instanceof Instance) { CompositeType comptype = ((Instance) this).getComposite() .getCompType(); for (RelationDefinition relDef : comptype.getCtxtRelations(this)) { if (!processed.contains(relDef.getName())) { relDefs.add(relDef); processed.add(relDef.getName()); } } } if (this instanceof Implementation) { for (CompositeType comptype : ((Implementation) this) .getInCompositeType()) { for (RelationDefinition relDef : comptype .getCtxtRelations(this)) { if (!processed.contains(relDef.getName())) { relDefs.add(relDef); processed.add(relDef.getName()); } } } } return relDefs; } @Override public Map<String, String> getValidAttributes() { Map<String, String> ret = new HashMap<String, String>(); for (PropertyDefinition def : declaration.getPropertyDefinitions()) { ret.put(def.getName(), def.getType()); } if (getGroup() != null) { ret.putAll(getGroup().getValidAttributes()); } return ret; } /** * Override to make hash code conform to the equality definition */ @Override public final int hashCode() { return componentId.hashCode(); } /** * Provided a component, compute its effective relations definition, adding group * constraint and flags. It is supposed to be correct !! No failure expected * * Does not add those dependencies defined "above". * For relations refined locally, merge the local definition with the group definition. * * For instances only, add the relation definition overridden by the composite. * * Remove those links that are not valid with the computed relation definition (for changeOwner) * */ private void initializeRelations() { /* * First we need to compute the list of relations that must be locally * defined in this component. We consider locally defined relation * declarations and overridden inherited relations. */ Set<RelationDeclaration> overrides = null; if (this instanceof Instance) { overrides = ((Instance) this).getComposite().getCompType().getCompoDeclaration().getOverridenDependencies(); } else overrides = Collections.emptySet() ; Set<RelationDeclaration> localRelations = new HashSet<RelationDeclaration>(); Set<String> processed = new HashSet<String>(); Component group = this; while (group != null) { for (RelationDeclaration relationDeclaration : group.getDeclaration().getRelations()) { /* * Ignore relations already processed at a lower level */ if (processed.contains(relationDeclaration.getIdentifier())) { continue; } /* * Check overridden relations */ boolean matchOverride = false; for (RelationDeclaration override : overrides) { // for (RelationDeclaration override : overrides != null ? overrides // : Collections.<RelationDeclaration> emptySet()) { if (matchOverride(relationDeclaration, override)) { relationDeclaration = relationDeclaration.overriddenBy(override); matchOverride = true; } } /* * Process locally declared and inherited overridden relations */ if (group == this || matchOverride) { localRelations.add(relationDeclaration); processed.add(relationDeclaration.getIdentifier()); } } group = group.getGroup(); } /* * Define all the local relations definition of this component */ for (RelationDeclaration relationDeclaration : localRelations) { /* * Local declarations may be partial definitions, we need to compute * the complete declaration by refining the ancestor definition. */ RelationDefinition base = this.getRelation(relationDeclaration.getIdentifier()); relationDeclaration = (base == null) ? relationDeclaration : ((RelationDefinitionImpl) base).refinedBy(relationDeclaration); relDef.put(relationDeclaration.getIdentifier(), new RelationDefinitionImpl(relationDeclaration)); } /* * If the component has links, remove those that are invalid (for changeOwner) * Also remove the link if it is a promotion, since we are no longer in the same composite */ for (Link localLink : getLocalLinks()) { if (localLink.isPromotion() || !localLink.isValid()) localLink.remove(); } for (Link incoming : getInvLinks()) { if (incoming.isPromotion() || !incoming.isValid()) incoming.remove(); } } /** * Given a relation declared in this component, checks if the provided * override relation matches the relation declaration. * * To be applied on a component C, the override must be such that : id * matches the override id source must be the name of C or of an ancestor of * C. target must be the same type (resource of component, and its name must * match). * */ public boolean matchOverride(RelationDeclaration relation, RelationDeclaration override) { // Overrides are currently only valid for instance boolean match = (this instanceof Instance); if (!match) { return false; } // Check if override source matches this component or one of its // ancestors match = false; Component group = this; while (group != null && !match) { match = override.refines(group.getDeclaration().getReference(), relation); group = group.getGroup(); } return match; } /** * To be called once the Apam entity is fully initialized. * * Computes all its attributes, including inheritance. Checks if initial properties are consistent with the * declarations. * * NOTE this method is also called when the owner changes, to force recalculation of substitutions that depend * on the current owner. */ private void initializeProperties(Map<String, String> initialProperties) { Component group = getGroup(); /* * Currently the instance declaration may include invalid properties. * * For declared instances this should not happen as the declaration is validated at build-time. For dynamically * created instances (using the APAM API or the apform API directly) this should be validated by this method. * * However, there are many properties added by the iPOJO apform layer that need to be ignored, so we simply * silently ignore all invalid properties. * * TODO We should be able to distinguish properties specified by the user that must be validated, from properties * used internally by the iPOJO apform. */ Set<String> invalidDeclaredProperties = new HashSet<String>(); for (String property : getDeclaration().getProperties().keySet()) { boolean isDefined = getDeclaration().isDefined(property) || (group != null && group.getPropertyDefinition(property) != null); if (!isDefined) { invalidDeclaredProperties.add(property); } if (group != null && group.getProperty(property) != null) { invalidDeclaredProperties.add(property); } } getDeclaration().getProperties().keySet().removeAll(invalidDeclaredProperties); /* * Merge initial and declared properties. * */ Map<String, String> fullInitialProperties = new HashMap<String, String>(getDeclaration().getProperties()); if (initialProperties != null) { fullInitialProperties.putAll(initialProperties); fullInitialProperties.remove("instance.name"); } /* * NOTE In the case of change owner, the initial properties include inherited values from the group, * we ignore them to avoid false error messages, they will be added later by the normal inheritance * mechanism * * TODO Distinguish the case of change owner from a real initialization */ if (initialProperties != null && group !=null) { for (Map.Entry<String, String> initialProperty : initialProperties.entrySet()) { if (group.getProperty(initialProperty.getKey()) != null && group.getProperty(initialProperty.getKey()).equals(initialProperty.getValue())) { fullInitialProperties.remove(initialProperty.getKey()); } } } // start cleaning the properties (normally empty) clear (); /* * First add the valid attributes. */ for (Map.Entry<String, String> initialProperty : fullInitialProperties.entrySet()) { PropertyDefinition def = validDef(initialProperty.getKey(), true); if (def != null) { Object val = Attribute.checkAttrType(initialProperty.getKey(), initialProperty.getValue(), def.getType()); if (val != null) { put(initialProperty.getKey(), val); } } } /* * then add those coming from its group, avoiding overloads. */ if (group != null) { for (String attr : group.getAllProperties().keySet()) { if (get(attr) == null) { put(attr, ((ComponentImpl)group).get(attr)); } } } /* * Add the default values specified in the group for properties not explicitly specified */ if (group != null) { for (PropertyDefinition definition : group.getDeclaration().getPropertyDefinitions()) { if (definition.hasDefaultValue() && get(definition.getName()) == null && definition.getInjected() != InjectedPropertyPolicy.INTERNAL) { Object val = Attribute.checkAttrType(definition.getName(),definition.getDefaultValue(), definition.getType()); if (val != null) { put(definition.getName(), val); } } } } /* * Set the attribute for the final attributes */ put(CST.SHARED, Boolean.toString(isShared())); put(CST.SINGLETON, Boolean.toString(isSingleton())); put(CST.INSTANTIABLE, Boolean.toString(isInstantiable())); /* * Finally add the specific attributes. * Should be the only place where instanceof is used. */ put(CST.NAME, apform.getDeclaration().getName()); if (this instanceof Specification) { put(CST.SPECNAME, apform.getDeclaration().getName()); } else if (this instanceof Implementation) { put(CST.IMPLNAME, apform.getDeclaration().getName()); if (this instanceof CompositeType) { put(CST.APAM_COMPOSITETYPE, CST.V_TRUE); } } else if (this instanceof Instance) { put(CST.INSTNAME, apform.getDeclaration().getName()); if (this instanceof Composite) { Composite composite = (Composite) this; put(CST.APAM_COMPOSITE, CST.V_TRUE); if (composite.getMainInst() != null) { put(CST.APAM_MAIN_INSTANCE, composite.getMainInst() .getName()); } } } /* * and propagate, to the platform and to members, in case the spec has been created after the implem */ for (Map.Entry<String, Object> entry : this.entrySet()) { for (Component member : getMembers()) { ((ComponentImpl) member).propagate(entry.getKey(), entry.getValue()); } } } /** * Whether this component is an ancestor of the specified component */ @Override public boolean isAncestorOf(Component member) { assert member != null; Component ancestor = member.getGroup(); while (ancestor != null && !ancestor.equals(this)) { ancestor = ancestor.getGroup(); } return ancestor != null; } /** * Whether this component is a descendant of the specified component */ @Override public boolean isDescendantOf(Component group) { assert group != null; return group.isAncestorOf(this); } public boolean isInternalInstantiable () { if (declaration.isDefinedInstantiable() || getGroup() == null) { return (declaration.isInstantiable()) ; } return ((ComponentImpl)getGroup()).isInternalInstantiable(); } /** * Whether the component can be instantiated */ @Override public boolean isInstantiable() { if (instantiateFails) return false ; if (!isInternalInstantiable()) return false ; if (this instanceof Implementation) { if (isSingleton() && !getMembers().isEmpty() ) return false; } return true ; } /** * Set the flag InstantiateFails which when true means : do not try to instantiate. * Should be used only by the resolver when createinstance fails * @param doNotTry */ public void setInstantiateFails (boolean doNotTryToInstantiate) { instantiateFails = doNotTryToInstantiate ; } /** * Whether the component is shared */ @Override public boolean isShared() { if (declaration.isDefinedShared() || getGroup() == null) { return declaration.isShared(); } return getGroup().isShared(); } /** * Whether the component is singleton */ @Override public boolean isSingleton() { if (declaration.isDefinedSingleton() || getGroup() == null) { return declaration.isSingleton(); } return getGroup().isSingleton(); } public boolean isSubstitute(String attr) { PropertyDefinition def = getDeclaration().getPropertyDefinition(attr); return (def != null && def.hasDefaultValue() && (def.getDefaultValue().charAt(0) == '$' || def.getDefaultValue().charAt(0) == '@')); } /** * The link keeps the relToResolve used for its resolution. * We make the hypothesis that the definition did not change, * and the source and destination did not change of composite. * * @param link * @return */ // public boolean isValidLink(Link link) { // return link.isValid() ; // RelToResolve relToResolve = link.getRelToResolve(); // if (relToResolve == null) return false ; // // // return relToResolve.matchRelationConstraints(link.getDestination()); // } @Override public boolean match(ApamFilter goal) { if (goal == null) { return true; } return goal.match(this); } @Override public boolean match(String goal) { if (goal == null) { return true; } ApamFilter f = ApamFilter.newInstance(goal); if (f == null) { return false; } return goal == null || f.match(this.getAllProperties()); } @Override public boolean matchRelation(RelToResolve dep) { return dep.matchRelation(this); } @Override public boolean matchRelationConstraints(RelToResolve dep) { return dep.matchRelationConstraints(dep.getTargetKind(), this.getAllProperties()); } @Override public boolean matchRelationTarget(RelToResolve dep) { return dep.matchRelationTarget(this); } /** * set the value, update apform and the platform, notify managers and * propagates to the members, recursively * * @param com * the component on which ot set the attribute * @param attr * attribute name * @param value * attribute value */ private void propagate(String attr, Object value) { if (value == null) { return; } Object oldValue = get(attr); if (oldValue != null && oldValue.equals(value.toString())) { return; } // Change value put(attr, value); // Notify the execution platform if (get(attr) == value) { getApformComponent().setProperty(attr, value.toString()); } /* * notify property managers */ if (oldValue == null) { ApamManagers.notifyAttributeAdded(this, attr, value.toString()); } else { ApamManagers.notifyAttributeChanged(this, attr, value.toString(), oldValue.toString()); } // Propagate to members recursively for (Component member : getMembers()) { ((ComponentImpl) member).propagate(attr, value); } } /* * Filter evaluation on the properties of this component */ /** * TODO. Should we notify at all levels ? * * @param ent * @param attr */ private void propagateRemove(String attr) { remove(attr); for (Component member : getMembers()) { ((ComponentImpl) member).propagateRemove(attr); } } /** * This methods adds a newly created component to the Apam state model, so * that it is visible to the external API */ public abstract void register(Map<String, String> initialProperties) throws InvalidConfiguration; public void removeInvLink(Link link) { invlinks.remove(link); // if (invLinks.isEmpty()) { /* * This instance is no longer used. We do not set it unused * setUsed(false); setOwner(CompositeImpl.getRootAllComposites()); * * Because it must stay in the same composite since it may be the target * of an "OWN" clause, and must not be changed. In case it will be * re-used (local). */ // } } // @Override public void removeLink(Link link) { if (getApformComponent().remLink(link.getDestination(), link.getName())) { links.remove(link); // TODO distriman: check if we have the destination implementation, // is this the right way to do it? // if(link.getDestination().getImpl()!=null) // ((ImplementationImpl) // getImpl()).removeUses(link.getDestination().getImpl()); } else { logger.error("INTERNAL ERROR: link from " + this + " to " + link.getDestination() + " could not be removed in the real instance."); } } /** * Removes the specifed property * */ @Override public boolean removeProperty(String attr) { return removeProperty(attr, false); } /** * Warning: to be used only by Apform for removing internal attributes. Only * Inhibits the message "Attribute " + attr + * " is an program field attribute and cannot be removed."); */ public boolean removeProperty(String attr, boolean forced) { String oldValue = getProperty(attr); if (oldValue == null) { logger.error("ERROR: \"" + attr + "\" not instanciated"); return false; } if (Attribute.isFinalAttribute(attr)) { logger.error("ERROR: \"" + attr + "\" is a final attribute"); return false; } if (Attribute.isReservedAttributePrefix(attr)) { logger.error("ERROR: \"" + attr + "\" is a reserved attribute"); return false; } PropertyDefinition propDef = getAttrDefinition(attr); if (propDef != null && propDef.getField() != null && !forced) { logger.error("In " + this + " attribute " + attr + " is a program field and cannot be removed."); return false; } if (getGroup() != null && getGroup().getProperty(attr) != null) { logger.error("In " + this + " attribute " + attr + " inherited and cannot be removed."); return false; } // it is ok, remove it and propagate to members, recursively propagateRemove(attr); // TODO. Should we notify at all levels ? ApamManagers.notifyAttributeRemoved(this, attr, oldValue); return true; } /** * Sets all the values of the specified properties * * We validate all attributes before actually modifying the value to avoid * partial modifications ? */ @Override public boolean setAllProperties(Map<String, String> properties) { for (Map.Entry<String, String> entry : properties.entrySet()) { if (!setProperty(entry.getKey(), entry.getValue())) { return false; } } return true; } public void setFirstDeployed(CompositeType father) { firstDeployed = father; } /** * Set the value of the property in the Apam state model. Changing an * attribute notifies the property manager, the underlying execution * platform (apform), and propagates to members */ @Override public boolean setProperty(String attr, Object value) { return setProperty(attr, value, false); } /** * Warning: to be used only by Apform for setting internal attributes. Only * Inhibits the message "Attribute " + attr + * " is an internal field attribute and cannot be set."); * * @param attr * @param value * @param forced * @return */ public boolean setProperty(String attr, Object value, boolean forced) { /* * Validate that the property is defined and the value is valid Forced * means that we can set field attribute */ PropertyDefinition def = validDef(attr, forced); if (def == null) { return false; } // At initialization, all valid attributes are ok for specs Object val = Attribute.checkAttrType(attr, value, def.getType()); if (val == null) { return false; } /* * Force recalculation of dependencies that may have been invalidated by * the property change. This must be done before notification and * propagation, otherwise we risk to remove links updated by managers. * * We remove only those links that are now invalid problematic for those * that are not lazy links : assychronous messages, .. and avoiding * unnecessary work TODO Check if this must be done for all links or * only dynamic links */ Object oldValue = get(attr); put(attr, val); for (Link incoming : getInvLinks()) { // If still valid, do nothing if (incoming.hasConstraints() && !incoming.isValid()) { incoming.remove(); } } // If outgoing constraints have substitution, the link may be now invalid for (Link outgoing : getLocalLinks()) { // if (!!!((RelationDefinitionImpl) outgoing.getRelDefinition()).isStaticImplemConstraints() // && !outgoing.isValid()) { outgoing.getRelToResolve().reComputeSubstFilters(); if (!outgoing.isValid()) { outgoing.remove(); } } // WARNING : undo the change because propagate needs the initial state if (oldValue == null) { remove(attr); } else { put(attr, oldValue); } // does the change, notifies managers, changes the platform and // propagate to members this.propagate(attr, val); return true; } // ============================================== @Override public String toString() { return getKind() + " " + getName(); } /** * This method removes a component from the Apam state model, it must ensure * that the component is no longer referenced by any other component or * visible by the external API. Should be called ONLY from the Broker. */ abstract void unregister(); /** * An attribute is valid if declared in an ancestor, and not set in an * ancestor. Check if the value is conforming to its type (string, int, * boolean). Internal attribute (associated with a field) cannot be set. * Must be called on the level above. * * Checks if attr is correctly defined for component ent it must be * explicitly defined in its upper groups, for the top group, it must be * already existing. * * boolean Forced = true can be set only by Apform. Needed when an internal * attribute is set in the program, Apform propagates the value to the * attribute. * * @param attr * @param value * @return */ public PropertyDefinition validDef(String attr, boolean forced) { if (Attribute.isFinalAttribute(attr) ) { logger.error("In " + this + ", cannot redefine final attribute \"" + attr + "\""); return null; } if (Attribute.isReservedAttributePrefix(attr)) { logger.error("In " + this + ", attribute\"" + attr + "\" is reserved"); return null; } PropertyDefinition definition = this.getAttrDefinition(attr); if (definition == null ) { logger.error("In " + this + ", attribute \"" + attr + "\" is undefined."); return null; } /* * Internal field attributes cannot be set */ if (definition.getInjected() == InjectedPropertyPolicy.INTERNAL && !forced) { logger.error("In " + this + ", attribute \"" + attr + "\" is an internal field attribute and cannot be set."); return null; } /* * if the same attribute exists above, it is a redefinition. */ ComponentImpl group = (ComponentImpl) this.getGroup(); if (group != null && group.get(attr) != null) { Object groupValue = group.get(attr); String defaultValue = definition.getDefaultValue(); boolean isDefault = defaultValue != null && groupValue.equals(Attribute.checkAttrType(attr,defaultValue,definition.getType())); // If the attribute above is the default value, it is allowed to change it if (!isDefault && !Attribute.isBuiltAttribute(attr)) { logger.error("In " + this + ", cannot redefine attribute \"" + attr + "\""); return null; } } return definition; } }