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 }