/** * 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.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import org.apache.felix.ipojo.Pojo; import org.osgi.framework.BundleException; 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.ComponentBroker; import fr.imag.adele.apam.Composite; import fr.imag.adele.apam.CompositeType; import fr.imag.adele.apam.Implementation; import fr.imag.adele.apam.Instance; import fr.imag.adele.apam.Specification; import fr.imag.adele.apam.apform.Apform2Apam; import fr.imag.adele.apam.apform.ApformCompositeType; import fr.imag.adele.apam.apform.ApformImplementation; import fr.imag.adele.apam.apform.ApformInstance; import fr.imag.adele.apam.apform.ApformSpecification; import fr.imag.adele.apam.declarations.SpecificationDeclaration; import fr.imag.adele.apam.declarations.references.ResolvableReference; import fr.imag.adele.apam.declarations.references.resources.ResourceReference; import fr.imag.adele.apam.impl.ComponentImpl.InvalidConfiguration; import fr.imag.adele.apam.util.ApamFilter; import fr.imag.adele.apam.util.ApamInstall; public class ComponentBrokerImpl implements ComponentBroker { /** * An special apform specification created only for those specifications * that do not exist in the Apform ipojo layer. Creates a minimal definition * structure. */ private static class ApamOnlySpecification extends BaseApformComponent<Specification, SpecificationDeclaration> implements ApformSpecification { public ApamOnlySpecification(String name, Set<ResourceReference> resources, Map<String, String> properties) { super(new SpecificationDeclaration(name)); declaration.getProvidedResources().addAll(resources); if (properties != null) { declaration.getProperties().putAll(properties); } } } private static Logger logger = LoggerFactory.getLogger(ComponentBrokerImpl.class); /* * The three main table to maintain. */ private final Set<Specification> specifications = Collections.newSetFromMap(new ConcurrentHashMap<Specification, Boolean>()); private final Set<Implementation> implementations = Collections.newSetFromMap(new ConcurrentHashMap<Implementation, Boolean>()); private final Set<Instance> instances = Collections.newSetFromMap(new ConcurrentHashMap<Instance, Boolean>()); /** * The task executor. We use a pool of a threads to handle component removal,as this may block as a side-efect and may cause deadlocks * specially at platform shutdown. */ private final Executor executor = Executors.newCachedThreadPool(new ThreadPoolFactory("unregistration", Executors.defaultThreadFactory())); /** * The task in charge of performing asynchronous removal */ private class UnregisterTask implements Runnable { private final Component component; public UnregisterTask(Component component) { this.component = component; } @Override public void run() { /* * Remove the component from the model and notify managers * * TODO perhaps we should notify managers before actually destroying the component * graph, this will have implications as navigation in the manager may recreate * deleted edges and have other side-effects */ ((ComponentImpl)component).unregister(); ApamManagers.notifyRemovedFromApam(component); } } /** * Asynchronously destroy a component */ private void disappearedComponent(Component component) { try { Set<? extends Component> collection = null; switch (component.getKind()) { case IMPLEMENTATION: collection = implementations; break; case INSTANCE: collection = instances; break; case SPECIFICATION: collection = specifications; break; default: break; } if (collection == null) { return; } /* * First remove from the collection, so that is no longer visible for resolution */ boolean exists = collection.remove(component); /* * Remove the component from the model and notify managers * * TODO perhaps we should notify managers before actually destroying the component * graph, this will have implications as navigation in the manager may recreate * deleted edges and have other side-effects */ if (exists) { executor.execute(new UnregisterTask(component)); } } catch (Exception unexpected) { } } /** * a component disappeared in the platform; we have to delete it from the * ASM. */ public void disappearedComponent(String componentName) { Component component = getComponent(componentName); if (component == null) { return; } disappearedComponent(component); } void add(Implementation implementation) { assert implementation != null && !implementations.contains(implementation); implementations.add(implementation); } void add(Instance instance) { assert instance != null && !instances.contains(instance); instances.add(instance); } void add(Specification spec) { assert spec != null; specifications.add(spec); } @Override public Implementation addImpl(CompositeType composite, ApformImplementation apfImpl) { // assert apfImpl != null; if (apfImpl == null) { logger.error("Error adding implementation: null Apform instance"); return null; } String implementationName = apfImpl.getDeclaration().getName(); Implementation implementation = getImpl(implementationName); if (implementation != null) { logger.error("Error adding implementation: already exists" + implementationName); return implementation; } if (composite == null) { composite = CompositeTypeImpl.getRootCompositeType(); } /* * Create and register the object in the APAM state model */ try { // create a primitive or composite implementation if (apfImpl instanceof ApformCompositeType) { implementation = new CompositeTypeImpl(composite, (ApformCompositeType) apfImpl); } else { implementation = new ImplementationImpl(composite, apfImpl); } ((ImplementationImpl) implementation).register(null); return implementation; } catch (InvalidConfiguration configurationError) { logger.error("Error adding implementation: exception in APAM registration, with message: {}", configurationError.getMessage()); } return null; } @Override public Instance addInst(Composite composite, ApformInstance apfInst) { if (apfInst == null) { logger.error("Error adding instance: null Apform instance"); return null; } String instanceName = apfInst.getDeclaration().getName(); String implementationName = apfInst.getDeclaration().getImplementation().getName(); Instance instance = getInst(instanceName); if (instance != null) { logger.error("Error adding instance: already exists " + instanceName); return instance; } /* * Get the implementation group to create the instance. If the * implementation can not be resolved the creation of the instance * fails. */ Implementation implementation = getImpl(implementationName); if (implementation == null) { logger.error("Error adding instance: implementation not found" + implementationName); return null; } if (composite == null) { composite = CompositeImpl.getRootAllComposites(); } /* * Create and register the object in the APAM state model */ try { instance = ((ImplementationImpl) implementation).reify(composite, apfInst); ((InstanceImpl) instance).register(null); return instance; } catch (Exception instantiationError) { /* * TODO START CLONE * * This is a clone of exactly the same code in Implementation.createInstance, we should unify treatment */ /* * TODO this should be done in method register, we should try to have some form * of atomic registration that includes registration and addition in the broker */ if (instance != null) { if (CST.componentBroker.getComponent(instance.getName()) != null) { /* * If creation failed after broker registration, undo registration */ ((ComponentBrokerImpl) CST.componentBroker).disappearedComponent(instance); } else { /* * The instance was partially created, just undo registration */ ((ComponentImpl)instance).unregister(); } } /* * TODO END CLONE */ logger.error("Error adding instance: exception registering instance in APAM ",instantiationError); } return null; } @Override public Specification addSpec(ApformSpecification apfSpec) { if (apfSpec == null) { logger.error("Error adding specification: null Apform"); return null; } String specificationName = apfSpec.getDeclaration().getName(); Specification specification = getSpec(specificationName); if (specification != null) { logger.error("Error adding specification: already exists " + specificationName); return specification; } /* * Create and register the object in the APAM state model */ try { specification = new SpecificationImpl(apfSpec); ((SpecificationImpl) specification).register(null); return specification; } catch (InvalidConfiguration configurationError) { logger.error("Error adding specification: exception in apam registration", configurationError); } return null; } /* * Probably only a bundle remove, that does the same as update. Uninstall a * bundle, and removes all the components inside that bundle. */ @Override public void componentBundleRemove(Component component) { if (component == null) { return; } try { component.getApformComponent().getBundle().uninstall(); } catch (BundleException e) { logger.error("could not unsintall component " + component.getName()); e.printStackTrace(); } } @Override public Implementation createImpl(CompositeType compo, String implName, URL url, Map<String, String> properties) { assert implName != null && url != null; Implementation impl = getImpl(implName); if (impl != null) { // do not create twice return impl; } impl = ApamInstall.intallImplemFromURL(url, implName); if (impl == null) { logger.error("deployment failed :" + implName + " at URL " + url); return null; } if (compo != null && !((CompositeTypeImpl) compo).isSystemRoot()) { ((CompositeTypeImpl) compo).deploy(impl); } return impl; } @Override public Specification createSpec(String specName, Set<ResourceReference> resources, Map<String, String> properties) { assert specName != null && resources != null; return addSpec(new ApamOnlySpecification(specName, resources, properties)); } @Override public Component getComponent(String name) { if (name == null) { return null; } for (Specification spec : specifications) { if (name.equals(spec.getName())) { return spec; } } for (Implementation impl : implementations) { if (name.equals(impl.getName())) { return impl; } } for (Instance inst : instances) { if (name.equals(inst.getName())) { return inst; } } return null; } public boolean contains(Component component) { switch (component.getDeclaration().getKind()) { case SPECIFICATION: return specifications.contains(component); case IMPLEMENTATION: return implementations.contains(component); case INSTANCE: return instances.contains(component); default: return false; } } @Override public Implementation getImpl(String name) { if (name == null) { return null; } for (Implementation impl : implementations) { if (name.equals(impl.getName())) { return impl; } } return null; } @Override public Set<Implementation> getImpls() { return Collections.unmodifiableSet(implementations); } @Override public Set<Implementation> getImpls(String goal) { if (goal == null) { return getImpls(); } Set<Implementation> ret = new HashSet<Implementation>(); ApamFilter f = ApamFilter.newInstance(goal); for (Implementation impl : implementations) { if (impl.match(f)) { ret.add(impl); } } return ret; } // /Remove @Override public Instance getInst(String instName) { if (instName == null) { return null; } for (Instance inst : instances) { if (inst.getName().equals(instName)) { return inst; } } return null; } @Override public Set<Instance> getInsts() { return Collections.unmodifiableSet(instances); } @Override public Set<Instance> getInsts(String goal) { if (goal == null) { return getInsts(); } Set<Instance> ret = new HashSet<Instance>(); ApamFilter f = ApamFilter.newInstance(goal); for (Instance inst : instances) { if (inst.match(f)) { ret.add(inst); } } return ret; } // /Add internal to package @Override public Instance getInstService(Object service) { /* * If this is a Apam native component (iPOJO+APAM) then just follow the * chained references */ if (service instanceof Pojo) { Pojo pojo = (Pojo) service; if (pojo.getComponentInstance() instanceof ApformInstance) { ApformInstance apform = (ApformInstance) pojo.getComponentInstance(); return apform.getApamComponent(); } } // it is a legacy, use brute force for (Instance inst : instances) { if (inst.getServiceObject() == service) { return inst; } } return null; } @Override public Specification getSpec(String name) { if (name == null) { return null; } for (Specification spec : specifications) { if (name.equals(spec.getName())) { return spec; } } return null; } @Override public Specification getSpecResource(ResolvableReference resource) { for (Specification spec : specifications) { // Verify if the requested resource is the spec itself if (spec.getDeclaration().getReference().equals(resource)) { return spec; } // Verify if the requested resource is provided by the spec if (spec.getDeclaration().getProvidedResources().contains(resource)) { return spec; } } return null; } @Override public Set<Specification> getSpecs() { return new HashSet<Specification>(specifications); } @Override public Set<Specification> getSpecs(String goal) { if (goal == null) { return getSpecs(); } Set<Specification> ret = new HashSet<Specification>(); ApamFilter f = ApamFilter.newInstance(goal); for (Specification spec : specifications) { if (spec.match(f)) { ret.add(spec); } } return ret; } @Override public Component getWaitComponent(String name) { return getWaitComponent(name, 0); } // /special case @Override public Component getWaitComponent(String name, long timeout) { Component compo = getComponent(name); if (compo != null) { return compo; } /* * If not found wait and try again */ Apform2Apam.waitForComponent(name, timeout); compo = getComponent(name); if (compo == null) {// occur when timeout elapsed logger.debug("wake up but component is not present " + name); } return compo; } }