001package org.dcm4che3.conf.api.upgrade;
002
003import org.dcm4che3.conf.api.DicomConfiguration;
004import org.dcm4che3.conf.core.api.*;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008import java.util.Map;
009import java.util.Properties;
010
011/**
012 * Implement this interface to cover
013 * <ul>
014 * <li>transformation of configuration classes structure between releases</li>
015 * <li>conditional default config initialization</li>
016 * <li>migration of legacy configuration</li>
017 * </ul>
018 * <p/>
019 * Mark the implemented class with
020 * <code>@ org.dcm4che3.conf.api.upgrade.ScriptVersion</code>
021 * annotation to allow the upgrade runner to detect whether the script needs to be re-executed.
022 */
023public interface UpgradeScript {
024    String NO_VERSION = "-NO_VERSION-";
025
026    void upgrade(UpgradeContext upgradeContext) throws ConfigurationException;
027
028    class UpgradeContext {
029
030        private static final Logger log = LoggerFactory.getLogger(UpgradeContext.class);
031
032        private String fromVersion;
033        private String toVersion;
034        private Properties properties;
035        private Map<String, Object> scriptConfig;
036        private Configuration configuration;
037        private DicomConfiguration dicomConfiguration;
038        private UpgradeScriptMetadata upgradeScriptMetadata;
039        private ConfigurationMetadata configMetaData;
040
041        public UpgradeContext() {
042        }
043
044        public UpgradeContext(String fromVersion, String toVersion, Properties properties, Map<String, Object> scriptConfig, Configuration configuration, DicomConfiguration dicomConfiguration) {
045            this(fromVersion, toVersion, properties, scriptConfig, configuration, dicomConfiguration, null, null);
046        }
047
048        public UpgradeContext(String fromVersion, String toVersion, Properties properties, Map<String, Object> scriptConfig, Configuration configuration, DicomConfiguration dicomConfiguration, UpgradeScriptMetadata upgradeScriptMetadata) {
049            this(fromVersion, toVersion, properties, scriptConfig, configuration, dicomConfiguration, upgradeScriptMetadata, null);
050        }
051
052        public UpgradeContext(String fromVersion, String toVersion, Properties properties, Map<String, Object> scriptConfig, Configuration configuration, DicomConfiguration dicomConfiguration, UpgradeScriptMetadata upgradeScriptMetadata, ConfigurationMetadata configMetaData) {
053            this.fromVersion = fromVersion;
054            this.toVersion = toVersion;
055            this.properties = properties;
056            this.scriptConfig = scriptConfig;
057            this.configuration = configuration;
058            this.dicomConfiguration = dicomConfiguration;
059            this.upgradeScriptMetadata = upgradeScriptMetadata;
060            this.configMetaData = configMetaData;
061        }
062
063
064        public Object getFromVersion() {
065            return fromVersion;
066        }
067
068        public Object getToVersion() {
069            return toVersion;
070        }
071
072        public Configuration getConfiguration() {
073            return configuration;
074        }
075
076        public DicomConfiguration getDicomConfiguration() {
077            return dicomConfiguration;
078        }
079
080        public Properties getProperties() {
081            return properties;
082        }
083
084        public Map<String, Object> getScriptConfig() {
085            return scriptConfig;
086        }
087
088        public UpgradeScriptMetadata getUpgradeScriptMetadata() {
089            return upgradeScriptMetadata;
090        }
091
092        /**
093         * Allows to "fork" another script from the currently executed script.<br><br>
094         * This feature allows to refactor an existing script (script <b>A</b>) by extracting a part of it into a new script (script <b>B</b>). <br><br>
095         * To facilitate a simpler design, this method will pre-set some version for that new script (<b>B</b>), so the new script will "think"
096         * that it's not the first time it's executed.
097         * This covers the case when upgrading from an older version of <b>A</b> (when A+B was still a single script), so <b>B</b> will not think that it's a "clean installation" <br><br>
098         * <b>CAUTION:</b> It is important that <b>B</b> appears <i>after</i> <b>A</b> in upgrade-settings.json
099         * </b>
100         *
101         * @param forkedScript  The <b>B</b> script.
102         * @param scriptVersion The version to set for the <b>B</b> script.
103         */
104        public void forkScript(Class<? extends UpgradeScript> forkedScript, String scriptVersion) {
105
106            String scriptClass = forkedScript.getName();
107
108            UpgradeScriptMetadata upgradeScriptMetadata = configMetaData.getMetadataOfUpgradeScripts().get(scriptClass);
109
110            if (upgradeScriptMetadata != null) {
111                log.warn("Attempted to fork script [" + scriptClass + "] that has already been executed (last executed version " + upgradeScriptMetadata.getLastVersionExecuted() + ")");
112                return;
113            }
114
115            UpgradeScriptMetadata scriptMetadata = new UpgradeScriptMetadata();
116            scriptMetadata.setLastVersionExecuted(scriptVersion);
117
118            configMetaData.getMetadataOfUpgradeScripts().put(scriptClass, scriptMetadata);
119            log.info("Forked upgrade script [" + scriptClass + "], initialized with version " + scriptVersion);
120
121        }
122    }
123
124    @ConfigurableClass
125    class UpgradeScriptMetadata {
126
127        /**
128         * The version of this upgrade script when it was last time executed, taken from @ScriptVersion
129         */
130        @ConfigurableProperty(description = "The version of this upgrade script when it was last time executed")
131        String lastVersionExecuted;
132
133        public String getLastVersionExecuted() {
134            return lastVersionExecuted;
135        }
136
137        public void setLastVersionExecuted(String lastVersionExecuted) {
138            this.lastVersionExecuted = lastVersionExecuted;
139        }
140    }
141}