/** * Stops an instance (prerequisite: DEPLOYED_STARTED). * * @param instance the instance * @param plugin the associated plug-in * @throws IOException if something went wrong */ void stop(Instance instance, PluginInterface plugin) throws IOException { String instancePath = InstanceHelpers.computeInstancePath(instance); if (instance.getStatus() != InstanceStatus.DEPLOYED_STARTED) this.logger.fine( instancePath + " cannot be stopped. Prerequisite status: DEPLOYED_STARTED (but was " + instance.getStatus() + ")."); else stopInstance(instance, plugin, false); }
/** * Deploys an instance (prerequisite: NOT_DEPLOYED). * * @param instance the instance * @param plugin the associated plug-in * @param fileNameToFileContent a map containing resources for the plug-in * @throws IOException if something went wrong */ void deploy(Instance instance, PluginInterface plugin, Map<String, byte[]> fileNameToFileContent) throws IOException { String instancePath = InstanceHelpers.computeInstancePath(instance); if (instance.getStatus() != InstanceStatus.NOT_DEPLOYED) { this.logger.fine( instancePath + " cannot be deployed. Prerequisite status: NOT_DEPLOYED (but was " + instance.getStatus() + ")."); } else if (instance.getParent() != null && instance.getParent().getStatus() != InstanceStatus.DEPLOYED_STARTED && instance.getParent().getStatus() != InstanceStatus.DEPLOYED_STOPPED && instance.getParent().getStatus() != InstanceStatus.UNRESOLVED && instance.getParent().getStatus() != InstanceStatus.WAITING_FOR_ANCESTOR) { this.logger.fine( instancePath + " cannot be deployed because its parent is not deployed. Parent status: " + instance.getParent().getStatus() + "."); } else { this.logger.fine("Deploying instance " + instancePath); // User reporting => deploying... instance.setStatus(InstanceStatus.DEPLOYING); try { this.messagingClient.sendMessageToTheDm( new MsgNotifInstanceChanged(this.appName, instance)); // Clean up the potential remains of a previous installation AgentUtils.deleteInstanceResources(instance); // Copy the resources AgentUtils.copyInstanceResources(instance, fileNameToFileContent); // Initialize the plugin plugin.initialize(instance); // Invoke the plug-in plugin.deploy(instance); instance.setStatus(InstanceStatus.DEPLOYED_STOPPED); this.messagingClient.sendMessageToTheDm( new MsgNotifInstanceChanged(this.appName, instance)); } catch (Exception e) { this.logger.severe("An error occured while deploying " + instancePath); Utils.logException(this.logger, e); instance.setStatus(InstanceStatus.NOT_DEPLOYED); this.messagingClient.sendMessageToTheDm( new MsgNotifInstanceChanged(this.appName, instance)); } } }
/** * Undeploys an instance (prerequisite: DEPLOYED_STOPPED). * * @param instance the instance * @param plugin the associated plug-in * @throws IOException if something went wrong */ void undeploy(Instance instance, PluginInterface plugin) throws IOException { // Preliminary check String instancePath = InstanceHelpers.computeInstancePath(instance); if (instance.getStatus() != InstanceStatus.DEPLOYED_STOPPED && instance.getStatus() != InstanceStatus.UNRESOLVED && instance.getStatus() != InstanceStatus.WAITING_FOR_ANCESTOR) { this.logger.fine( instancePath + " cannot be undeployed. Prerequisite status: DEPLOYED_STOPPED or UNRESOLVED or WAITING_FOR_ANCESTOR (but was " + instance.getStatus() + ")."); return; } // Children may have to be marked as stopped. // From a plug-in point of view, we only use the one for the given instance. // Children are SUPPOSED to be stopped immediately. List<Instance> instancesToUndeploy = InstanceHelpers.buildHierarchicalList(instance); Collections.reverse(instancesToUndeploy); InstanceStatus newStatus = InstanceStatus.NOT_DEPLOYED; try { // Update the statuses if necessary for (Instance i : instancesToUndeploy) { if (i.getStatus() == InstanceStatus.NOT_DEPLOYED) continue; i.setStatus(InstanceStatus.UNDEPLOYING); this.messagingClient.sendMessageToTheDm(new MsgNotifInstanceChanged(this.appName, i)); this.messagingClient.unpublishExports(i); } // Hypothesis: undeploying an instance undeploys its children too. try { plugin.undeploy(instance); // Delete files for undeployed instances for (Instance i : instancesToUndeploy) AgentUtils.deleteInstanceResources(i); } catch (PluginException e) { this.logger.severe( "An error occured while undeploying " + InstanceHelpers.computeInstancePath(instance)); Utils.logException(this.logger, e); newStatus = InstanceStatus.DEPLOYED_STOPPED; } } catch (Exception e) { newStatus = InstanceStatus.DEPLOYED_STOPPED; Utils.logException(this.logger, e); } finally { // Update the status of all the instances for (Instance i : instancesToUndeploy) { i.setStatus(newStatus); this.messagingClient.sendMessageToTheDm(new MsgNotifInstanceChanged(this.appName, i)); } } }
/** * Builds the right handler depending on the current instance's state. * * @param instance an instance * @param appName the application name * @param messagingClient the messaging client * @return a non-null manager to update the instance's life cycle */ public static AbstractLifeCycleManager build( Instance instance, String appName, IAgentClient messagingClient) { AbstractLifeCycleManager result; switch (instance.getStatus()) { case DEPLOYED_STARTED: result = new DeployedStarted(appName, messagingClient); break; case DEPLOYED_STOPPED: result = new DeployedStopped(appName, messagingClient); break; case NOT_DEPLOYED: result = new NotDeployed(appName, messagingClient); break; case UNRESOLVED: result = new Unresolved(appName, messagingClient); break; case WAITING_FOR_ANCESTOR: result = new WaitingForAncestor(appName, messagingClient); break; default: result = new TransitiveStates(appName, messagingClient); break; } return result; }
/** * Stops an instance. * * <p>If necessary, it will stop its children. * * @param instance an instance (must be deployed and started) * @param plugin the plug-in to use for concrete modifications * @param isDueToImportsChange true if this method is called because an import changed, false it * matches a manual life cycle change * @throws IOException if something went wrong */ private void stopInstance(Instance instance, PluginInterface plugin, boolean isDueToImportsChange) throws IOException { this.logger.fine("Stopping instance " + InstanceHelpers.computeInstancePath(instance) + "..."); // Children may have to be stopped too. // From a plug-in point of view, we only use the one for the given instance. // Children are SUPPOSED to be stopped immediately. List<Instance> instancesToStop = InstanceHelpers.buildHierarchicalList(instance); Collections.reverse(instancesToStop); try { // Update the statuses if necessary for (Instance i : instancesToStop) { if (i.getStatus() != InstanceStatus.DEPLOYED_STARTED) continue; i.setStatus(InstanceStatus.STOPPING); this.messagingClient.sendMessageToTheDm(new MsgNotifInstanceChanged(this.appName, i)); this.messagingClient.listenToRequestsFromOtherAgents(ListenerCommand.STOP, i); this.messagingClient.unpublishExports(i); } // Stop the initial instance. // Even if the plugin invocation fails, we cannot consider these instances // to be reliable anymore. So, we keep on as if the operation went well. try { plugin.stop(instance); } catch (PluginException e) { this.logger.severe( "An error occured while stopping " + InstanceHelpers.computeInstancePath(instance)); Utils.logException(this.logger, e); } } finally { List<Instance> forNotifications = new ArrayList<>(); for (Instance i : instancesToStop) { if (i.getStatus() != InstanceStatus.STOPPING && i.getStatus() != InstanceStatus.UNRESOLVED) continue; // Not due to import change? => stopped. InstanceStatus newStatus; if (!isDueToImportsChange) newStatus = InstanceStatus.DEPLOYED_STOPPED; // If we deal with the instance whose dependencies changed => unresolved. else if (i == instance) newStatus = InstanceStatus.UNRESOLVED; // Otherwise, we deal with a child instance. else newStatus = InstanceStatus.WAITING_FOR_ANCESTOR; i.setStatus(newStatus); forNotifications.add(i); } // Notify at the end for (Instance i : forNotifications) this.messagingClient.sendMessageToTheDm(new MsgNotifInstanceChanged(this.appName, i)); } }
/** * Starts an instance (prerequisite: DEPLOYED_STOPPED). * * @param instance the instance * @param plugin the associated plug-in * @throws IOException if something went wrong */ void start(Instance instance, PluginInterface plugin) throws IOException { String instancePath = InstanceHelpers.computeInstancePath(instance); if (instance.getStatus() != InstanceStatus.DEPLOYED_STOPPED && instance.getStatus() != InstanceStatus.WAITING_FOR_ANCESTOR) { this.logger.fine( instancePath + " cannot be started. Prerequisite status: DEPLOYED_STOPPED or WAITING_FOR_ANCESTOR (but was " + instance.getStatus() + ")."); } else if (instance.getParent() != null && instance.getParent().getStatus() != InstanceStatus.DEPLOYED_STARTED) { // An element cannot start if its parent is not started. // However, if the parent is unresolved (or waiting for its parent to start), // then we can mark this instance as ready to start with its parent. this.logger.fine( instancePath + " cannot be started because its parent is not started. Parent status: " + instance.getParent().getStatus() + "."); if (instance.getParent().getStatus() == InstanceStatus.UNRESOLVED || instance.getParent().getStatus() == InstanceStatus.WAITING_FOR_ANCESTOR) { instance.setStatus(InstanceStatus.WAITING_FOR_ANCESTOR); this.logger.fine(instancePath + " will start as soon as its parent starts."); } } else { // Otherwise, start it try { if (ImportHelpers.hasAllRequiredImports(instance, this.logger)) { instance.data.put(FORCE, "whatever"); updateStateFromImports(instance, plugin, null, null); } else { this.logger.fine( "Instance " + InstanceHelpers.computeInstancePath(instance) + " cannot be started, dependencies are missing. Requesting exports from other agents."); instance.setStatus(InstanceStatus.UNRESOLVED); this.messagingClient.sendMessageToTheDm( new MsgNotifInstanceChanged(this.appName, instance)); this.messagingClient.requestExportsFromOtherAgents(instance); } } catch (PluginException e) { this.logger.severe( "An error occured while starting " + InstanceHelpers.computeInstancePath(instance)); Utils.logException(this.logger, e); instance.setStatus(InstanceStatus.DEPLOYED_STOPPED); this.messagingClient.sendMessageToTheDm( new MsgNotifInstanceChanged(this.appName, instance)); } } }
/** * Updates the status of an instance based on the imports. * * @param impactedInstance the instance whose imports may have changed * @param plugin the plug-in to use to apply a concrete modification * @param statusChanged The changed status of the instance that changed (e.g. that provided new * imports) * @param importChanged The individual imports that changed */ public void updateStateFromImports( Instance impactedInstance, PluginInterface plugin, Import importChanged, InstanceStatus statusChanged) throws IOException, PluginException { // Do we have all the imports we need? boolean haveAllImports = ImportHelpers.hasAllRequiredImports(impactedInstance, this.logger); // Update the life cycle of this instance if necessary // Maybe we have something to start if (haveAllImports) { if (impactedInstance.getStatus() == InstanceStatus.UNRESOLVED || impactedInstance.data.remove(FORCE) != null) { InstanceStatus oldState = impactedInstance.getStatus(); impactedInstance.setStatus(InstanceStatus.STARTING); try { this.messagingClient.sendMessageToTheDm( new MsgNotifInstanceChanged(this.appName, impactedInstance)); plugin.start(impactedInstance); impactedInstance.setStatus(InstanceStatus.DEPLOYED_STARTED); this.messagingClient.sendMessageToTheDm( new MsgNotifInstanceChanged(this.appName, impactedInstance)); this.messagingClient.publishExports(impactedInstance); this.messagingClient.listenToRequestsFromOtherAgents( ListenerCommand.START, impactedInstance); } catch (Exception e) { this.logger.severe( "An error occured while starting " + InstanceHelpers.computeInstancePath(impactedInstance)); Utils.logException(this.logger, e); impactedInstance.setStatus(oldState); this.messagingClient.sendMessageToTheDm( new MsgNotifInstanceChanged(this.appName, impactedInstance)); } } else if (impactedInstance.getStatus() == InstanceStatus.DEPLOYED_STARTED) { plugin.update(impactedInstance, importChanged, statusChanged); } else { this.logger.fine( InstanceHelpers.computeInstancePath(impactedInstance) + " checked import changes but has nothing to update (1)."); } } // Or maybe we have something to stop else { if (impactedInstance.getStatus() == InstanceStatus.DEPLOYED_STARTED) { stopInstance(impactedInstance, plugin, true); } else { this.logger.fine( InstanceHelpers.computeInstancePath(impactedInstance) + " checked import changes but has nothing to update (2)."); } } }