Skip to content

Commit a442a85

Browse files
Merge pull request #14 from BorderTech/touchfile
Touchfile
2 parents b676622 + a4373d5 commit a442a85

File tree

8 files changed

+329
-32
lines changed

8 files changed

+329
-32
lines changed

pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<parent>
1111
<groupId>com.github.bordertech.common</groupId>
1212
<artifactId>qa-parent</artifactId>
13-
<version>1.0.9</version>
13+
<version>1.0.10</version>
1414
</parent>
1515

1616
<packaging>jar</packaging>
@@ -64,4 +64,5 @@
6464
</dependency>
6565
</dependencies>
6666

67+
6768
</project>

spotbugs-exclude-filter.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
<!-- False Positive on Loading Property Filenames -->
55
<Match>
6-
<Class name="com.github.bordertech.config.DefaultConfiguration"/>
76
<Bug pattern="PATH_TRAVERSAL_IN" />
87
</Match>
98

src/main/java/com/github/bordertech/config/Config.java

Lines changed: 91 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.apache.commons.configuration.CompositeConfiguration;
1212
import org.apache.commons.configuration.Configuration;
1313
import org.apache.commons.configuration.MapConfiguration;
14+
import org.apache.commons.lang.StringUtils;
1415

1516
/**
1617
* The Config class is the central access point to the configuration mechanism, and is used to read or modify the
@@ -34,22 +35,46 @@
3435
* <li><code>bordertech-local.properties</code> - local developer properties</li>
3536
* </ul>
3637
*
38+
* <p>
39+
* A touchfile can be set via the parameter <code>bordertech.config.touchfile</code>. The touchfile is checked when
40+
* {@link #getInstance()} is called. To avoid excessive IO an interval (in milli seconds) between checks can be set via
41+
* <code>bordertech.config.touchfile.interval</code> and defaults to <code>10000</code>.
42+
* </p>
43+
*
3744
* @author Joshua Barclay
3845
* @author Jonathan Austin
3946
* @since 1.0.0
47+
*
48+
* @see DefaultConfiguration
49+
* @see InitHelper
50+
* @see ConfigurationLoader
4051
*/
4152
public final class Config {
4253

4354
/**
44-
* The current configuration.
55+
* Used to lock the config.
4556
*/
46-
private static Configuration configuration = loadConfiguration();
57+
private static final Object LOCK = new Object();
4758

4859
/**
4960
* Contains the complete set of property change listeners that have registered with this class.
5061
*/
5162
private static final Set<PropertyChangeListener> PROPERTY_CHANGE_LISTENERS = new HashSet<>();
5263

64+
/**
65+
* The current configuration.
66+
*/
67+
private static Configuration configuration;
68+
69+
/**
70+
* Touchfile (if configured).
71+
*/
72+
private static Touchfile touchfile;
73+
74+
static {
75+
loadConfiguration();
76+
}
77+
5378
/**
5479
* Prevent instantiation of this utility class.
5580
*/
@@ -60,16 +85,41 @@ private Config() {
6085
* @return the current configuration.
6186
*/
6287
public static Configuration getInstance() {
88+
// If a touchfile has been set, check if it has changed and reload if necessary
89+
if (touchfile != null) {
90+
synchronized (LOCK) {
91+
if (touchfile.hasChanged()) {
92+
loadConfiguration();
93+
}
94+
}
95+
}
6396
return configuration;
6497
}
6598

6699
/**
67100
* Resets the configuration back to the default internal configuration. All configuration changes which have been
68101
* made will be lost. This method is primarily intended for unit testing.
69102
*/
70-
public static synchronized void reset() {
71-
configuration = loadConfiguration();
72-
notifyListeners();
103+
public static void reset() {
104+
synchronized (LOCK) {
105+
loadConfiguration();
106+
}
107+
}
108+
109+
/**
110+
* <p>
111+
* Sets the current configuration.</p>
112+
* <p>
113+
* <b>Warning: </b> this will ignore any defined ConfigurationLoaders</p>
114+
*
115+
* @param configuration the configuration to set.
116+
*/
117+
public static void setConfiguration(final Configuration configuration) {
118+
synchronized (LOCK) {
119+
Config.configuration = configuration;
120+
configTouchfile();
121+
notifyListeners();
122+
}
73123
}
74124

75125
/**
@@ -79,7 +129,7 @@ public static synchronized void reset() {
79129
* @return a copy of the given configuration.
80130
*/
81131
public static Configuration copyConfiguration(final Configuration original) {
82-
Configuration copy = new MapConfiguration(new HashMap<String, Object>());
132+
Configuration copy = new MapConfiguration(new HashMap<>());
83133

84134
for (Iterator<?> i = original.getKeys(); i.hasNext();) {
85135
String key = (String) i.next();
@@ -91,23 +141,9 @@ public static Configuration copyConfiguration(final Configuration original) {
91141

92142
copy.setProperty(key, value);
93143
}
94-
95144
return copy;
96145
}
97146

98-
/**
99-
* <p>
100-
* Sets the current configuration.</p>
101-
* <p>
102-
* <b>Warning: </b> this will ignore any defined ConfigurationLoaders</p>
103-
*
104-
* @param configuration the configuration to set.
105-
*/
106-
public static synchronized void setConfiguration(final Configuration configuration) {
107-
Config.configuration = configuration;
108-
notifyListeners();
109-
}
110-
111147
/**
112148
* This method notifies all the {@link PropertyChangeListener}s that have registered with this object that a change
113149
* has occurred.
@@ -147,11 +183,42 @@ public static void addPropertyChangeListener(final PropertyChangeListener listen
147183
}
148184

149185
/**
150-
* @return the configuration to use.
186+
* Load the configuration.
151187
*/
152-
private static synchronized Configuration loadConfiguration() {
188+
private static void loadConfiguration() {
153189
Configuration config = checkSLIConfiguration();
154-
return config == null ? getDefaultConfiguration() : config;
190+
if (config == null) {
191+
config = getDefaultConfiguration();
192+
}
193+
Config.configuration = config;
194+
configTouchfile();
195+
notifyListeners();
196+
}
197+
198+
/**
199+
* Configure the touchfile (if provided).
200+
*/
201+
private static void configTouchfile() {
202+
String file = getTouchFileName();
203+
if (StringUtils.isEmpty(file)) {
204+
touchfile = null;
205+
} else {
206+
touchfile = new Touchfile(file, getTouchFileInterval());
207+
}
208+
}
209+
210+
/**
211+
* @return the touch file name
212+
*/
213+
private static String getTouchFileName() {
214+
return configuration.getString("bordertech.config.touchfile");
215+
}
216+
217+
/**
218+
* @return the touch file interval (in milli seconds)
219+
*/
220+
private static long getTouchFileInterval() {
221+
return configuration.getLong("bordertech.config.touchfile.interval", 10000);
155222
}
156223

157224
/**
@@ -170,7 +237,7 @@ private static Configuration checkSLIConfiguration() {
170237

171238
// Use a CompositeConfiguration if there are custom ConfigurationLoader implementations.
172239
if (iterator.hasNext()) {
173-
CompositeConfiguration compositeConfig = new CompositeConfiguration(new MapConfiguration(new HashMap<String, Object>()));
240+
CompositeConfiguration compositeConfig = new CompositeConfiguration(new MapConfiguration(new HashMap<>()));
174241
while (iterator.hasNext()) {
175242
compositeConfig.addConfiguration(iterator.next().getConfiguration());
176243
}

src/main/java/com/github/bordertech/config/DefaultConfiguration.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
*
4040
* @author Jonathan Austin
4141
* @since 1.0.0
42+
*
43+
* @see Config
4244
*/
4345
public class DefaultConfiguration implements Configuration {
4446

@@ -316,7 +318,7 @@ private String getDebuggingInfo() {
316318
codesourceStr = codesource == null ? "" : " code location of ConfigImpl: " + codesource.getLocation();
317319
}
318320
} catch (Exception failed) {
319-
codesourceStr = "Could not determine location of ConfigImpl.";
321+
codesourceStr = "Could not determine location of ConfigImpl [" + failed.getMessage() + "].";
320322
}
321323

322324
StringBuilder info = new StringBuilder();
@@ -325,8 +327,7 @@ private String getDebuggingInfo() {
325327
info.append(codesourceStr);
326328
info.append("\nWorking directory is ");
327329
info.append(workingDir);
328-
info.append(
329-
"\nParameters have loaded, there is a full parameter dump in log4j FILE appender at ");
330+
info.append("\nParameters have loaded, there is a full parameter dump in log4j FILE appender at ");
330331
info.append(get(paramsFile));
331332
info.append("\nTo dump all params to stdout set ");
332333
info.append(DUMP);

src/main/java/com/github/bordertech/config/InitHelper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
*
3030
* @author Jonathan Austin
3131
* @since 1.0.0
32+
*
33+
* @see Config
3234
*/
3335
public final class InitHelper {
3436

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.github.bordertech.config;
2+
3+
import java.io.File;
4+
5+
/**
6+
* Monitor a touchfile to check if it has changed.
7+
*/
8+
public class Touchfile {
9+
10+
/**
11+
* Touchfile name.
12+
*/
13+
private final String filename;
14+
15+
/**
16+
* The touch file.
17+
*/
18+
private final File touchfile;
19+
20+
/**
21+
* Check interval in milli seconds.
22+
*/
23+
private final long checkInterval;
24+
25+
/**
26+
* The last time the file was checked.
27+
*/
28+
private long lastChecked;
29+
30+
/**
31+
* The files last modified time.
32+
*/
33+
private long lastModified;
34+
35+
/**
36+
* @param filename the touch file name
37+
* @param checkInterval the interval to check the file in milli seconds
38+
*/
39+
public Touchfile(final String filename, final long checkInterval) {
40+
if (filename == null || filename.isEmpty()) {
41+
throw new IllegalArgumentException("A touch filename must be provided.");
42+
}
43+
this.filename = filename;
44+
this.touchfile = new File(filename);
45+
this.checkInterval = checkInterval < 0 ? 0 : checkInterval;
46+
this.lastChecked = System.currentTimeMillis();
47+
this.lastModified = touchfile.lastModified();
48+
}
49+
50+
/**
51+
*
52+
* @return true if touch file has changed
53+
*/
54+
public boolean hasChanged() {
55+
56+
// Has the check interval passed?
57+
long now = System.currentTimeMillis();
58+
if (now - lastChecked < checkInterval) {
59+
return false;
60+
}
61+
lastChecked = now;
62+
63+
// Has the file changed?
64+
long modified = touchfile.lastModified();
65+
if (lastModified == modified) {
66+
// No Change
67+
return false;
68+
}
69+
70+
// Has changed so save new file details
71+
lastModified = modified;
72+
return true;
73+
}
74+
75+
/**
76+
* @return the file name
77+
*/
78+
public final String getFilename() {
79+
return filename;
80+
}
81+
82+
/**
83+
* @return the check interval
84+
*/
85+
public final long getCheckInterval() {
86+
return checkInterval;
87+
}
88+
89+
/**
90+
* @return the last checked time
91+
*/
92+
public final long getLastChecked() {
93+
return lastChecked;
94+
}
95+
96+
/**
97+
* @return the file last modified
98+
*/
99+
public final long getLastModified() {
100+
return lastModified;
101+
}
102+
103+
}

src/test/java/com/github/bordertech/config/ConfigTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,27 @@
66

77
/**
88
* Ensure the Config class does what it says.
9+
*
910
* @author Rick Brown
1011
*/
1112
public class ConfigTest {
1213

1314
@Test
1415
public void testGetInstance() {
15-
System.out.println("getInstance");
1616
Configuration expResult = Config.getInstance();
1717
Configuration result = Config.getInstance();
1818
Assert.assertSame("The singleton should return the same instance", expResult, result);
1919
}
2020

2121
@Test
2222
public void testCopyConfiguration() {
23-
System.out.println("copyConfiguration");
2423
Configuration config = Config.getInstance();
2524
Configuration result = Config.copyConfiguration(config);
2625
Assert.assertNotSame("Copy should return a new instance", config, result);
2726
}
2827

2928
@Test
3029
public void testCopyConfigurationWithProps() {
31-
System.out.println("copyConfiguration");
3230
Configuration config = Config.getInstance();
3331
final String expected = "kungfu";
3432
config.setProperty("kung.fu", expected);

0 commit comments

Comments
 (0)