/**
* 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.HashSet;
import java.util.Set;
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.CompositeType;
import fr.imag.adele.apam.DeploymentManager;
import fr.imag.adele.apam.DynamicManager;
import fr.imag.adele.apam.Implementation;
import fr.imag.adele.apam.Link;
import fr.imag.adele.apam.RelToResolve;
import fr.imag.adele.apam.RelationManager;
import fr.imag.adele.apam.Resolved;
import fr.imag.adele.apam.Specification;
import fr.imag.adele.apam.apform.Apform2Apam;
/**
* This is a manager that handles intermediate states arriving when a resolution is
* requested for a component being updated.
*
* It implements a simple strategy that blocks all resolutions targeting the components
* being updated.
*
* @author vega
*
*/
public class UpdateMan implements RelationManager, DynamicManager {
private static Set<String> installingComponents = new HashSet<String>();
static Logger logger = LoggerFactory.getLogger(UpdateMan.class);
public UpdateMan() {
}
@Override
public String getName() {
return CST.UPDATEMAN;
}
/**
* The component compo needs to be updated. Compo must be existing. We will
* be looking for a bundle with same id as the one from which compo was
* initially loaded. If no such bundle is found, does nothing: it is not an
* update, but a normal installation.
*
* Since all the components in the previous bundle will be uninstalled, we
* have to record the components that will be re-installed in order to wait
* for these new versions to be ready, in case the client accesses the
* variable during the bundle loading.
*
* @param compo
* @return
*/
public static void updateComponent(Implementation component) {
try {
// return the composite type that physically deployed the bundle
CompositeType compoTypeFrom = component.getFirstDeployed();
logger.info("Updating implementation " + component.getName() + " in composite " + compoTypeFrom);
for (DeploymentManager manager : ApamManagers.getDeploymentManagers()) {
logger.debug(manager.getName() + " ");
DeploymentManager.Unit deployed = manager.getDeploymentUnit(compoTypeFrom, component);
if (deployed != null && deployed.getComponents().contains(component.getName())) {
// it is indeed a deployment
UpdateMan.addInstallingComponents(deployed);
/**
* WARNING: The new bundle may not start if the new
* bundle has a new package relation not currently
* satisfied
*/
deployed.update();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* This method is to be called when a bundle is about to be updated. The new
* bundle contains the components whose name pertain to the set
* "sel.getComponents()" . Therefore, any find or resolution related to
* these components should wait for the component to be available. WARNING:
* if the component never appears, the client may be locked forever.
*
* @param sel
* : the information about the bundle to deploy.
*/
private static void addInstallingComponents(DeploymentManager.Unit installingUnit) {
if (installingUnit == null || installingUnit.getComponents().isEmpty()) {
System.out.println("no component to update ???");
return;
}
/*
* Be sure that the list is atomically updated
*/
synchronized (installingComponents) {
installingComponents.addAll(installingUnit.getComponents());
}
}
/**
* Must be called when a component appears. If it was an update, its name is
* in the "deployed" set, and it must be removed from that list.
*
* @param component
*/
@Override
public void addedComponent(Component newComponent) {
logger.debug("Added : " + newComponent);
/*
* notifications can originate concurrently with updates, so we need to
* synchronize access to the list of currently updating components.
*/
synchronized (installingComponents) {
installingComponents.remove(newComponent.getName());
}
}
@Override
public void removedComponent(Component lostComponent) {
logger.debug("Removed : " + lostComponent);
}
@Override
public void addedLink(Link wire) {
}
@Override
public void removedLink(Link wire) {
}
/**
* This is an INTERNAL manager that will be invoked by the core.
*
* So in this method we signal that we are not part of the external handlers to
* invoke for this resolution request.
*
*/
@Override
public boolean beginResolving(RelToResolve dep) {
return false;
}
@Override
public Resolved<?> resolve(RelToResolve dep) {
Specification spec = CST.componentBroker.getSpecResource(dep.getTarget());
if (spec == null) {
return null;
}
waitComponent(spec.getName());
return null;
}
/**
* The current caller requires the component with name "name". If this
* component is under deployment, the current thread must wait until the
* component appears.
*
* @param name
*/
private void waitComponent(String name) {
/*
* First try the fast case when there is no pending updates for this
* component
*/
synchronized (installingComponents) {
if (!installingComponents.contains(name)) {
return;
}
}
/*
* we wait for the component.
*
* INPORTANT Notice that we wait outside the synchronized block, because
* we must let notifications proceed while we wait. Otherwise this would
* lead to a deadlock between the thread requiring the component and the
* thread performing the deployment.
*/
logger.info("Waiting for " + name + " update.");
Apform2Apam.waitForComponent(name);
logger.info(name + " update done.");
}
// when in Felix.
public void start() {
System.out.println("UpdateMan started");
}
public void stop() {
}
}