/**
* 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());
}
}