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}