001/**
002 * Copyright 2010-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.common.util.runonce.smart;
017
018import static com.google.common.base.Preconditions.checkArgument;
019import static com.google.common.base.Preconditions.checkNotNull;
020import static com.google.common.base.Preconditions.checkState;
021
022import java.io.File;
023import java.util.Properties;
024
025import org.apache.commons.lang3.StringUtils;
026import org.kuali.common.util.PropertyUtils;
027import org.kuali.common.util.file.CanonicalFile;
028import org.kuali.common.util.log.LoggerUtils;
029import org.slf4j.Logger;
030
031public final class PropertiesFileRunOnce implements RunOnce {
032
033        private static final Logger logger = LoggerUtils.make();
034
035        private final File file;
036        private final String encoding;
037        private final String key;
038
039        private Properties properties;
040        private boolean runonce;
041        private boolean initialized = false;
042
043        @Override
044        public synchronized void initialize() {
045                checkState(!initialized, "Already initialized");
046                logger.info("--- Initializing properties file backed RunOnce ---");
047                this.properties = getProperties();
048                this.runonce = getBoolean(properties, key);
049                showConfig();
050                logger.info("--- Properties file backed RunOnce initialized. ---");
051                this.initialized = true;
052        }
053
054        @Override
055        public synchronized boolean isTrue() {
056                checkState(initialized, "Not initialized");
057                return runonce;
058        }
059
060        @Override
061        public synchronized void changeState(RunOnceState state) {
062                checkState(initialized, "Not initialized");
063                checkNotNull(state, "'state' cannot be null");
064                properties.setProperty(key, state.name());
065                PropertyUtils.store(properties, file, encoding);
066                this.properties = PropertyUtils.load(file, encoding);
067                this.runonce = getBoolean(properties, key);
068                checkState(!isTrue(), "isTrue() must return false");
069                logger.info("Transitioned RunOnce to - [{}]", state.name());
070        }
071
072        private boolean getBoolean(Properties properties, String key) {
073                String value = properties.getProperty(key);
074                return Boolean.parseBoolean(value);
075        }
076
077        protected void showConfig() {
078                logger.info("Properties file: [{}]", file);
079                logger.info("Properties file exists: {}", file.exists());
080                logger.info("Property: [{}]=[{}]", key, properties.get(key));
081                logger.info("RunOnce: [{}]", runonce);
082        }
083
084        protected Properties getProperties() {
085                if (file.exists()) {
086                        return PropertyUtils.load(file, encoding);
087                } else {
088                        return new Properties();
089                }
090        }
091
092        private PropertiesFileRunOnce(Builder builder) {
093                this.file = builder.file;
094                this.encoding = builder.encoding;
095                this.key = builder.key;
096        }
097
098        public static Builder builder(File file, String encoding, String key) {
099                return new Builder(file, encoding, key);
100        }
101
102        public static class Builder {
103
104                private final File file;
105                private final String key;
106                private final String encoding;
107
108                public Builder(File file, String encoding, String key) {
109                        this.file = new CanonicalFile(file);
110                        this.encoding = encoding;
111                        this.key = key;
112                }
113
114                public PropertiesFileRunOnce build() {
115                        PropertiesFileRunOnce instance = new PropertiesFileRunOnce(this);
116                        validate(instance);
117                        return instance;
118                }
119
120                private void validate(PropertiesFileRunOnce instance) {
121                        checkNotNull(instance.getFile(), "file cannot be null");
122                        checkArgument(!StringUtils.isBlank(instance.getEncoding()), "encoding cannot be blank");
123                        checkArgument(!StringUtils.isBlank(instance.getKey()), "key cannot be blank");
124                }
125        }
126
127        public File getFile() {
128                return file;
129        }
130
131        public String getEncoding() {
132                return encoding;
133        }
134
135        public String getKey() {
136                return key;
137        }
138
139}