/** * 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.net.URL; 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.CST; import fr.imag.adele.apam.Component; import fr.imag.adele.apam.Composite; import fr.imag.adele.apam.CompositeType; import fr.imag.adele.apam.ContextualManager; import fr.imag.adele.apam.Implementation; import fr.imag.adele.apam.ManagerModel; import fr.imag.adele.apam.RelationDefinition; import fr.imag.adele.apam.Specification; import fr.imag.adele.apam.apform.ApformCompositeType; import fr.imag.adele.apam.apform.ApformInstance; import fr.imag.adele.apam.declarations.CompositeDeclaration; import fr.imag.adele.apam.declarations.RelationDeclaration; public class CompositeTypeImpl extends ImplementationImpl implements CompositeType { private static Logger logger = LoggerFactory.getLogger(CompositeTypeImpl.class); private static final long serialVersionUID = 1L; /** * The root of the composite type hierarchy */ private static CompositeType rootCompoType; /** * NOTE We can not directly initialize the field because the constructor may * throw an exception, so we need to make a static block to be able to catch * the exception. The root composite bootstraps the system, so normally we * SHOULD always be able to create it; if there is an exception, that means * there is some bug and we cannot continue so we throw a class * initialization exception. */ static { CompositeType bootstrap = null; try { bootstrap = new CompositeTypeImpl(); } catch (Exception e) { throw new ExceptionInInitializerError(e); } finally { rootCompoType = bootstrap; } } private static Map<String, CompositeType> compositeTypes = new ConcurrentHashMap<String, CompositeType>(); public static CompositeType getCompositeType(String name) { return CompositeTypeImpl.compositeTypes.get(name); } public static Collection<CompositeType> getCompositeTypes() { return Collections.unmodifiableCollection(CompositeTypeImpl.compositeTypes.values()); } public static CompositeType getRootCompositeType() { return CompositeTypeImpl.rootCompoType; } /** * The list of all composites in the APAM state model */ public static Collection<CompositeType> getRootCompositeTypes() { return CompositeTypeImpl.rootCompoType.getEmbedded(); } /* * The models associated to this composite type to specify the different * strategies to handle the instances of this type. */ private Set<ManagerModel> models = new HashSet<ManagerModel>(); /* * The contained implementations deployed (really or logically) by this * composite type. * * WARNING An implementation may be deployed by more than one composite type */ private Set<Implementation> contains = Collections.newSetFromMap(new ConcurrentHashMap<Implementation, Boolean>()); private Implementation mainImpl = null; /* * The hierarchy of composite types. * * This is a subset of the contains hierarchy restricted only to composites. */ private Set<CompositeType> embedded = Collections.newSetFromMap(new ConcurrentHashMap<CompositeType, Boolean>()); private Set<CompositeType> invEmbedded = Collections.newSetFromMap(new ConcurrentHashMap<CompositeType, Boolean>()); /* * all the dependencies between composite types */ private Set<CompositeType> imports = Collections.newSetFromMap(new ConcurrentHashMap<CompositeType, Boolean>()); private Set<CompositeType> invImports = Collections.newSetFromMap(new ConcurrentHashMap<CompositeType, Boolean>()); private static Map<String, RelationDefinition> ctxtDependencies = new HashMap<String, RelationDefinition>(); /** * This is an special constructor only used for the root type of the system */ private CompositeTypeImpl() throws InvalidConfiguration { super(CST.ROOT_COMPOSITE_TYPE); /* * Look for platform models in directory "load" */ this.models = new HashSet<ManagerModel>(); /* * Look for specified root configuration for managers */ for (Map.Entry<String,URL> managerConfiguration : CST.rootConfiguration.entrySet()) { models.add(new ManagerModel(managerConfiguration.getKey(),managerConfiguration.getValue())); } } /** * Builds a new Apam composite type to represent the specified * implementation in the Apam model. */ protected CompositeTypeImpl(CompositeType composite, ApformCompositeType apfCompo) throws InvalidConfiguration { super(composite, apfCompo); /* * Reference the enclosing composite hierarchy */ addInvEmbedded(composite); /* * Get declared models */ this.models.addAll(apfCompo.getModels()); } public void addEmbedded(CompositeType destination) { embedded.add(destination); } public void addImpl(Implementation impl) { contains.add(impl); } @Override public void addImport(CompositeType destination) { imports.add(destination); ((CompositeTypeImpl) destination).addInvImport(this); } public void addInvEmbedded(CompositeType origin) { invEmbedded.add(origin); } public void addInvImport(CompositeType dependent) { invImports.add(dependent); } @Override public boolean containsImpl(Implementation impl) { return contains.contains(impl); } /** * Deploy (logically) a new implementation into this composite type. * * TODO Should this method be in the public API or it is restricted to the * resolver and other managers? */ public void deploy(Implementation impl) { /* * Remove implementation from the unused container if this is the first * deployment */ if (!impl.isUsed()) { ((ImplementationImpl) impl).removeInComposites(CompositeTypeImpl.getRootCompositeType()); ((CompositeTypeImpl) CompositeTypeImpl.getRootCompositeType()).removeImpl(impl); /* * If the implementation is composite, it is also embedded in the * unused container */ if (impl instanceof CompositeType) { ((CompositeTypeImpl) impl).removeInvEmbedded(CompositeTypeImpl.getRootCompositeType()); ((CompositeTypeImpl) CompositeTypeImpl.getRootCompositeType()).removeEmbedded((CompositeTypeImpl) impl); } } /* * Add the implementation to this composite */ ((ImplementationImpl) impl).addInComposites(this); this.addImpl(impl); /* * Embed in this hierarchy if the implementation is composite */ if (impl instanceof CompositeType) { ((CompositeTypeImpl) impl).addInvEmbedded(this); this.addEmbedded((CompositeTypeImpl) impl); } } @Override public CompositeDeclaration getCompoDeclaration() { return (CompositeDeclaration) getDeclaration(); } /** * The ctxt relation must have the same Id, must indicate a source that is * an ancestor of parameter source, ans source must be of the right kind. * * It is supposed that a ctxtdep has an Id and a source. * * @param source * @param id * @return */ @Override public RelationDefinition getCtxtRelation(Component source, String id) { RelationDefinition dep = ctxtDependencies.get(id); if (dep == null) { return null; } Component group = source; while (group != null) { if (dep.appliesTo(group.getDeclaration().getReference())) { return dep; } group = group.getGroup(); } return null; } /** * The ctxt relation must have the same Id, must indicate a source that is * an ancestor of parameter source, ans source must be of the right kind. * * It is supposed that a ctxtdep has an Id and a source. * * @param source * @param id * @return */ @Override public Set<RelationDefinition> getCtxtRelations(Component source) { Set<RelationDefinition> deps = new HashSet<RelationDefinition>(); for (RelationDefinition dep : ctxtDependencies.values()) { Component group = source; boolean applies = false; while (group != null && ! applies) { if (dep.appliesTo(group.getDeclaration().getReference())) { deps.add(dep); applies = true; } group = group.getGroup(); } } return deps; } @Override public Set<CompositeType> getEmbedded() { return Collections.unmodifiableSet(embedded); } @Override public Set<Implementation> getImpls() { return Collections.unmodifiableSet(contains); } @Override public Set<CompositeType> getImport() { return Collections.unmodifiableSet(imports); } @Override public Set<CompositeType> getInvEmbedded() { return Collections.unmodifiableSet(invEmbedded); } @Override public Implementation getMainImpl() { return mainImpl; } @Override public ManagerModel getModel(ContextualManager manager) { for (ManagerModel model : models) { if (model.getManagerName().equals(manager.getName())) { return model; } } return null; } @Override public ManagerModel getModel(String managerName) { for (ManagerModel model : models) { if (model.getManagerName().equals(managerName)) { return model; } } return null; } @Override public Set<ManagerModel> getModels() { return Collections.unmodifiableSet(models); } @Override public boolean isFriend(CompositeType destination) { return imports.contains(destination); } /** * Whether this is the system root composite type */ public boolean isSystemRoot() { return this == rootCompoType; } @Override public void register(Map<String, String> initialProperties) throws InvalidConfiguration { /* * Opposite references from the enclosing composite types */ for (CompositeType inComposite : invEmbedded) { ((CompositeTypeImpl) inComposite).addEmbedded(this); } /* * Notify managers of their models. * * WARNING Notice that the managers are not notified at the end of the * registration, but before resolving the main implementation. This * allow the resolution of the main implementation in the context of the * composite, specially if the main implementation is deployed in the * private repository of the composite.. * * Managers must be aware that the composite type is not completely * registered, so they must be cautious when manipulating the state and * navigating the hierarchy. */ for (ContextualManager manager : ApamManagers.getContextualManagers()) { manager.initializeContext(this); } /* * Resolve main implementation. Does nothing if it is an abstract * composite. */ resolveMainImplem(); /* * add to list of composite types */ CompositeTypeImpl.compositeTypes.put(getName(), this); /* * Compute Dependencies from relation declarations. * TODO I am not sure this is needed */ for (RelationDeclaration relation : ((CompositeDeclaration) this.getDeclaration()).getContextualDependencies()) { // Build the corresponding relation and attach it to the component ctxtDependencies.put(relation.getIdentifier(), new RelationDefinitionImpl(relation)); } /* * Complete normal registration */ super.register(initialProperties); } @Override protected Composite reify(Composite composite, ApformInstance platformInstance) throws InvalidConfiguration { return new CompositeImpl(composite, platformInstance); } public boolean removeEmbedded(CompositeType destination) { return embedded.remove(destination); } public void removeImpl(Implementation impl) { contains.remove(impl); } public boolean removeImport(CompositeType destination) { ((CompositeTypeImpl) destination).removeInvImport(this); return imports.remove(destination); } public boolean removeInvEmbedded(CompositeType origin) { return invEmbedded.remove(origin); } public boolean removeInvImport(CompositeType dependent) { return invImports.remove(dependent); } /** * Resolve main implementation. * * First we try to find an implementation with the name of the main * component, if we fail to find one we assume the name corresponds to a * specification which is resolved. Notice that resolution of the main * component is done in the context of this composite type, so it will be * deployed in this context if necessary. * * WARNING this is done after the composite type is added to the hierarchy * but before it is completely registered as a normal implementation. We do * not call super.register until the main implementation is resolved. * */ private void resolveMainImplem() throws InvalidConfiguration { /* * Abstract Composite */ if (getCompoDeclaration().getMainComponent() == null) { if (!getProvidedResources().isEmpty()) { unregister(); throw new InvalidConfiguration("invalid composite type " + getName() + ". No Main implementation but provides resources " + getProvidedResources()); } return ; } String mainComponent = getCompoDeclaration().getMainComponent().getName(); mainImpl = CST.apamResolver.findImplByName(this, mainComponent); if (mainImpl != null) { logger.debug("The main component of " + this.getName() + " is an Implementation : " + mainImpl.getName()); } else { Specification spec = CST.apamResolver.findSpecByName(this, mainComponent); if (spec != null) { logger.debug("The main component of " + this.getName() + " is a Specification : " + spec.getName()); /* * It is a specification to resolve as the main implem. Do not * select another composite */ Set<String> constraints = new HashSet<String>(); constraints.add("(!(" + CST.APAM_COMPOSITETYPE + "=" + CST.V_TRUE + "))"); mainImpl = CST.apamResolver.resolveSpecByName(null, mainComponent, constraints, null); } } /* * If we cannot resolve the main implementation, we abort the * registration in APAM, taking care of undoing the partial processing * already performed. */ if (mainImpl == null) { unregister(); throw new InvalidConfiguration("Cannot find main implementation " + mainComponent + " for composite type " + getName()); } // assert mainImpl != null; if (!mainImpl.getInCompositeType().contains(this)) { deploy(mainImpl); } /* * Check that the main implementation conforms to the declaration of the * composite */ boolean providesResources = mainImpl.getProvidedResources().containsAll(getProvidedResources()); /* * If the main implementation is not conforming, we abort the * registration in APAM, taking care of undoing the partial processing * already performed. */ if (!providesResources) { unregister(); throw new InvalidConfiguration("invalid main implementation " + mainImpl.getName() + " for composite type " + getName() + "\n Main implementation Provided resources " + mainImpl.getDeclaration().getProvidedResources() + "do no provide all the expected resources : " + getSpec().getDeclaration().getProvidedResources()); } return ; } @Override public String toString() { return "COMPOSITETYPE " + getName(); } @Override public void unregister() { /* * Remove the instances and notify managers */ super.unregister(); /* * Remove import relationships. NOTE We have to copy the list because we * update it while iterating it */ for (CompositeType imported : new HashSet<CompositeType>(imports)) { removeImport(imported); } for (CompositeType importedBy : new HashSet<CompositeType>(invImports)) { ((CompositeTypeImpl) importedBy).removeImport(this); } /* * Remove opposite references from embedding composite types * * TODO May be this should be done at the same type that the contains * hierarchy, but this will require a refactor of the superclass to have * a fine control on the order of the steps. */ for (CompositeType inComposite : invEmbedded) { ((CompositeTypeImpl) inComposite).removeEmbedded(this); } invEmbedded.clear(); /* * Remove from list of composite types */ CompositeTypeImpl.compositeTypes.remove(getName()); } }