/* * Copyright (C) 2013 Intel Corporation * All rights reserved. */ package com.intel.mtwilson.setup; import com.intel.dcsg.cpg.configuration.MutableConfiguration; import com.intel.dcsg.cpg.configuration.PropertiesConfiguration; import com.intel.dcsg.cpg.validation.Fault; import com.intel.dcsg.cpg.validation.Model; import java.util.ArrayList; import java.util.List; /** * Subclasses should use getConfiguration() to obtain existing configuration * information. Avoid using the "My" class because the application may be trying * to run a setup task with an alternative provided configuration and it would * be counter-intuitive for configuration to come from somewhere else. * Subclasses can write generated configuration properties back to the * MutableConfiguration object obtained from getConfiguration() and the * application is responsible for saving that into mtwilson.properties after * running the setup tasks. * It is expected that an application running multiple setup tasks in a * sequence will initialize them all with the same MutableConfiguration * instance so each step can build on previous steps. * * Generated properties that should not be saved into configuration but instead * should be only displayed to the user (like generated administrator password) * should be stored in private variables accessible with bean setters/getters. * The application is responsible for prompting the user for these if necessary * and for * displaying them if they were generated. * * An application could define an alternate configuration file or format with * such display-only parameters to make an install repeatable while making * a clear distinction between what should be saved permanently in configuration * files and what is simply saved to a file for automation purposes and needs * to be removed after setup. Such files should only be guaranteed to be reusable * across instances of the same version of mtwilson. * * * @author jbuhacoff */ public abstract class AbstractSetupTask implements SetupTask { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractSetupTask.class); // private transient Integer lastHashCode = null; private transient MutableConfiguration configuration = new PropertiesConfiguration(); private transient ArrayList<Fault> configurationFaults = new ArrayList<>(); private transient ArrayList<Fault> validationFaults = new ArrayList<>(); /** * The application should call isConfigured() before calling isValidated() * or run() on the setup task so the configure() method will always be * called before validate() and * before execute() and both validate() and execute() will only be called * if there are no configuration errors. * * The configure() method should check that all necessary configuration * is available (or generate it if possible) and log configuration * errors for situations that cannot be fixed automatically or require * user input. * * Typically the configure() method will store the configuration in either * private member variables for transient settings or into the provided MutableConfiguration * object itself for persistent settings. Those member variables and * saved configuration can then be accessed from validate() and execute(). * * The job of configure() is to ensure that all the settings needed * in order to complete execute() successfully are present and inform * the calling code if they are not present (by logging configuration faults) * * @throws Exception */ abstract protected void configure() throws Exception; /** * The validate() method relies on configuration prepared by configure() * and checks that whatever was supposed to happen in execute() either * happened successfully or for any other reason it's not required to * call execute(). It logs validation errors if there is any issue which * requires further configuration or calling execute(). * * @throws Exception */ abstract protected void validate() throws Exception; /** * The execute() method relies on configuration prepared by configure() * and is responsible for successful completion of the setup task. If * that cannot be accomplished it must throw an exception. * @throws Exception */ abstract protected void execute() throws Exception; @Override public void setConfiguration(MutableConfiguration configuration) { this.configuration = configuration; } public MutableConfiguration getConfiguration() { return configuration; } @Override public List<Fault> getConfigurationFaults() { return configurationFaults; } @Override public List<Fault> getValidationFaults() { return validationFaults; } @Override public boolean isValidated() { try { validationFaults.clear(); validate(); return validationFaults.isEmpty(); } catch(Exception e) { throw new ValidationException(e); } } @Override public boolean isConfigured() { try { configurationFaults.clear(); configure(); return configurationFaults.isEmpty(); } catch(Exception e) { throw new ConfigurationException(e); } } @Override public void run() { if( !isConfigured() ) { throw new IllegalStateException("Configuration required"); } try { execute(); } catch(Exception e) { log.error("Setup task error: {}", e.getMessage()); log.debug("Setup task error", e); throw new SetupException("Setup error", e); } if( !isValidated() ) { throw new IllegalStateException("Validation failed"); } } // following section is similar to cpg-validation ObjectModel ; the only reason we're not extending ObjectModel is that isValid() doesn't make sense for us - we have isConfigured() and isRequired() instead protected final void configuration(Fault fault) { configurationFaults.add(fault); } protected final void configuration(String description) { configurationFaults.add(new Fault(description)); } protected final void configuration(String format, Object... args) { configurationFaults.add(new Fault(format, args)); } protected final void configuration(Throwable e, String description) { configurationFaults.add(new Fault(e, description)); } protected final void configuration(Throwable e, String format, Object... args) { configurationFaults.add(new Fault(e, format, args)); } protected final void configuration(Model m, String format, Object... args) { configurationFaults.add(new Fault(m, format, args)); } protected final void validation(Fault fault) { validationFaults.add(fault); } protected final void validation(String description) { validationFaults.add(new Fault(description)); } protected final void validation(String format, Object... args) { validationFaults.add(new Fault(format, args)); } protected final void validation(Throwable e, String description) { validationFaults.add(new Fault(e, description)); } protected final void validation(Throwable e, String format, Object... args) { validationFaults.add(new Fault(e, format, args)); } protected final void validation(Model m, String format, Object... args) { validationFaults.add(new Fault(m, format, args)); } // convenience methods /* protected void requireNonEmptyString(String key) { String value = configuration.getString(key); if( value == null || value.isEmpty() ) { configuration("Missing required setting: %s", key); } } */ }