1 package org.kuali.common.util.runonce.smart; 2 3 import static com.google.common.base.Preconditions.checkArgument; 4 import static com.google.common.base.Preconditions.checkNotNull; 5 import static com.google.common.base.Preconditions.checkState; 6 7 import java.io.File; 8 import java.util.Properties; 9 10 import org.apache.commons.lang3.StringUtils; 11 import org.kuali.common.util.PropertyUtils; 12 import org.kuali.common.util.file.CanonicalFile; 13 import org.kuali.common.util.log.LoggerUtils; 14 import org.slf4j.Logger; 15 16 public final class PropertiesFileRunOnce implements RunOnce { 17 18 private static final Logger logger = LoggerUtils.make(); 19 20 private final File file; 21 private final String encoding; 22 private final String key; 23 24 private Properties properties; 25 private boolean runonce; 26 private boolean initialized = false; 27 28 @Override 29 public synchronized void initialize() { 30 checkState(!initialized, "Already initialized"); 31 logger.info("--- Initializing properties file backed RunOnce ---"); 32 this.properties = getProperties(); 33 this.runonce = getBoolean(properties, key); 34 showConfig(); 35 logger.info("--- Properties file backed RunOnce initialized. ---"); 36 this.initialized = true; 37 } 38 39 @Override 40 public synchronized boolean isTrue() { 41 checkState(initialized, "Not initialized"); 42 return runonce; 43 } 44 45 @Override 46 public synchronized void changeState(RunOnceState state) { 47 checkState(initialized, "Not initialized"); 48 checkNotNull(state, "'state' cannot be null"); 49 properties.setProperty(key, state.name()); 50 PropertyUtils.store(properties, file, encoding); 51 this.properties = PropertyUtils.load(file, encoding); 52 this.runonce = getBoolean(properties, key); 53 checkState(!isTrue(), "isTrue() must return false"); 54 logger.info("Transitioned RunOnce to - [{}]", state.name()); 55 } 56 57 private boolean getBoolean(Properties properties, String key) { 58 String value = properties.getProperty(key); 59 return Boolean.parseBoolean(value); 60 } 61 62 protected void showConfig() { 63 logger.info("Properties file: [{}]", file); 64 logger.info("Properties file exists: {}", file.exists()); 65 logger.info("Property: [{}]=[{}]", key, properties.get(key)); 66 logger.info("RunOnce: [{}]", runonce); 67 } 68 69 protected Properties getProperties() { 70 if (file.exists()) { 71 return PropertyUtils.load(file, encoding); 72 } else { 73 return new Properties(); 74 } 75 } 76 77 private PropertiesFileRunOnce(Builder builder) { 78 this.file = builder.file; 79 this.encoding = builder.encoding; 80 this.key = builder.key; 81 } 82 83 public static Builder builder(File file, String encoding, String key) { 84 return new Builder(file, encoding, key); 85 } 86 87 public static class Builder { 88 89 private final File file; 90 private final String key; 91 private final String encoding; 92 93 public Builder(File file, String encoding, String key) { 94 this.file = new CanonicalFile(file); 95 this.encoding = encoding; 96 this.key = key; 97 } 98 99 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 }