001package org.kuali.common.util.runonce.smart;
002
003import static com.google.common.base.Preconditions.checkArgument;
004import static com.google.common.base.Preconditions.checkNotNull;
005import static com.google.common.base.Preconditions.checkState;
006
007import java.io.File;
008import java.util.Properties;
009
010import org.apache.commons.lang3.StringUtils;
011import org.kuali.common.util.PropertyUtils;
012import org.kuali.common.util.file.CanonicalFile;
013import org.kuali.common.util.log.LoggerUtils;
014import org.slf4j.Logger;
015
016public final class PropertiesFileRunOnce implements RunOnce {
017
018        private static final Logger logger = LoggerUtils.make();
019
020        private final File file;
021        private final String encoding;
022        private final String key;
023
024        private Properties properties;
025        private boolean runonce;
026        private boolean initialized = false;
027
028        @Override
029        public synchronized void initialize() {
030                checkState(!initialized, "Already initialized");
031                logger.info("--- Initializing properties file backed RunOnce ---");
032                this.properties = getProperties();
033                this.runonce = getBoolean(properties, key);
034                showConfig();
035                logger.info("--- Properties file backed RunOnce initialized. ---");
036                this.initialized = true;
037        }
038
039        @Override
040        public synchronized boolean isTrue() {
041                checkState(initialized, "Not initialized");
042                return runonce;
043        }
044
045        @Override
046        public synchronized void changeState(RunOnceState state) {
047                checkState(initialized, "Not initialized");
048                checkNotNull(state, "'state' cannot be null");
049                properties.setProperty(key, state.name());
050                PropertyUtils.store(properties, file, encoding);
051                this.properties = PropertyUtils.load(file, encoding);
052                this.runonce = getBoolean(properties, key);
053                checkState(!isTrue(), "isTrue() must return false");
054                logger.info("Transitioned RunOnce to - [{}]", state.name());
055        }
056
057        private boolean getBoolean(Properties properties, String key) {
058                String value = properties.getProperty(key);
059                return Boolean.parseBoolean(value);
060        }
061
062        protected void showConfig() {
063                logger.info("Properties file: [{}]", file);
064                logger.info("Properties file exists: {}", file.exists());
065                logger.info("Property: [{}]=[{}]", key, properties.get(key));
066                logger.info("RunOnce: [{}]", runonce);
067        }
068
069        protected Properties getProperties() {
070                if (file.exists()) {
071                        return PropertyUtils.load(file, encoding);
072                } else {
073                        return new Properties();
074                }
075        }
076
077        private PropertiesFileRunOnce(Builder builder) {
078                this.file = builder.file;
079                this.encoding = builder.encoding;
080                this.key = builder.key;
081        }
082
083        public static Builder builder(File file, String encoding, String key) {
084                return new Builder(file, encoding, key);
085        }
086
087        public static class Builder {
088
089                private final File file;
090                private final String key;
091                private final String encoding;
092
093                public Builder(File file, String encoding, String key) {
094                        this.file = new CanonicalFile(file);
095                        this.encoding = encoding;
096                        this.key = key;
097                }
098
099                public PropertiesFileRunOnce build() {
100                        PropertiesFileRunOnce instance = new PropertiesFileRunOnce(this);
101                        validate(instance);
102                        return instance;
103                }
104
105                private void validate(PropertiesFileRunOnce instance) {
106                        checkNotNull(instance.getFile(), "file cannot be null");
107                        checkArgument(!StringUtils.isBlank(instance.getEncoding()), "encoding cannot be blank");
108                        checkArgument(!StringUtils.isBlank(instance.getKey()), "key cannot be blank");
109                }
110        }
111
112        public File getFile() {
113                return file;
114        }
115
116        public String getEncoding() {
117                return encoding;
118        }
119
120        public String getKey() {
121                return key;
122        }
123
124}