Skip to content

Commit 22269e1

Browse files
committed
atomic update
1 parent 463bc52 commit 22269e1

File tree

5 files changed

+59
-22
lines changed

5 files changed

+59
-22
lines changed

etleap/src/main/java/us/hxbc/etleap/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ public static void main(String[] args) throws MalformedURLException {
1111
}
1212

1313
URL url = new URL(args[0]);
14-
new UpdateService(url).start();
14+
new UpdateService(url).start(5 * 60 * 1024);
1515
}
1616
}

etleap/src/main/java/us/hxbc/etleap/UpdateService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ void update() {
4242
}
4343
}
4444

45-
public void start() {
45+
public void start(int sleep) {
4646
runner = new Thread(() -> {
4747
logger.info("started, polling {} every 5 minutes", url);
4848

@@ -55,7 +55,7 @@ public void start() {
5555
}
5656

5757
try {
58-
cv.wait(5 * 60 * 1000); // 5 minutes
58+
cv.wait(sleep);
5959
} catch (InterruptedException e) {
6060
continue;
6161
}

etleap/src/main/java/us/hxbc/etleap/config/FileConfig.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.nio.file.OpenOption;
1515
import java.nio.file.Path;
1616
import java.nio.file.Paths;
17+
import java.nio.file.StandardCopyOption;
1718
import java.nio.file.StandardOpenOption;
1819

1920
import static com.google.common.base.Throwables.propagate;
@@ -36,21 +37,37 @@ public class FileConfig {
3637
tokens.pushBack(); // give the closing brace to parent
3738
}
3839

40+
InputStream openStream() throws IOException {
41+
return getUpdatePath().openStream();
42+
}
43+
44+
void update(InputStream is) throws IOException {
45+
Path t = Files.createTempFile(getPath().getParent(), getPath().getFileName().toString(), ".tmp");
46+
47+
try (OutputStream os = Files.newOutputStream(t,
48+
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
49+
ByteStreams.copy(is, os);
50+
} catch (IOException e) {
51+
logger.error("unable to write file", e);
52+
}
53+
54+
try {
55+
Files.move(t, getPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
56+
} catch (IOException e) {
57+
logger.error("unable to rename", e);
58+
}
59+
}
60+
3961
void apply() {
40-
logger.info("replacing {} with {}", path, updatePath);
62+
logger.info("replacing {} with {}", getPath(), getUpdatePath());
4163
try {
42-
Files.createDirectories(path.getParent());
64+
Files.createDirectories(getPath().getParent());
4365
} catch (IOException e) {
4466
logger.error("unable to create path", e);
4567
}
4668

47-
try (InputStream is = updatePath.openStream()) {
48-
try (OutputStream os = Files.newOutputStream(path,
49-
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
50-
ByteStreams.copy(is, os);
51-
} catch (IOException e) {
52-
logger.error("unable to replace file", e);
53-
}
69+
try (InputStream is = openStream()) {
70+
update(is);
5471
} catch (IOException e) {
5572
logger.error("unable to download update", e);
5673
throw propagate(e);

etleap/src/test/java/us/hxbc/etleap/UpdateServiceTest.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,15 @@
44
import org.junit.Test;
55

66
import java.io.StringReader;
7-
import java.net.URL;
87

9-
import static org.mockito.Mockito.mock;
10-
import static org.mockito.Mockito.when;
8+
import static org.mockito.Mockito.*;
119

1210
public class UpdateServiceTest {
1311
UpdateService service;
1412

1513
@Before
1614
public void setUp() throws Exception {
1715
service = mock(UpdateService.class);
18-
}
19-
20-
@Test
21-
public void testUpdate() throws Exception {
2216
String data = "file { \"/tmp/hello\":\n" +
2317
"source => \"http://host.com/path/to/hello\"\n" +
2418
"}\n" +
@@ -27,6 +21,18 @@ public void testUpdate() throws Exception {
2721
"}";
2822

2923
when(service.openURL()).thenReturn(new StringReader(data));
24+
}
25+
26+
@Test
27+
public void testUpdate() throws Exception {
3028
service.update();
29+
verify(service, atLeast(1)).update();
30+
}
31+
32+
@Test
33+
public void testStart() throws Exception {
34+
service.start(1000);
35+
Thread.sleep(1500);
36+
service.stop();
3137
}
32-
}
38+
}

etleap/src/test/java/us/hxbc/etleap/config/FileConfigTest.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@
22

33
import org.junit.Before;
44
import org.junit.Test;
5+
import org.mockito.verification.VerificationMode;
56

7+
import java.io.ByteArrayInputStream;
68
import java.io.StreamTokenizer;
79
import java.io.StringReader;
810

911
import static org.assertj.core.api.Assertions.assertThat;
12+
import static org.mockito.Mockito.*;
1013

1114
public class FileConfigTest {
1215
FileConfig config;
1316

1417
@Before
1518
public void setup() throws Exception {
1619
String data = "\"/tmp/hello\":\n" +
17-
"source => \"http://host.com/path/to/hello\"\n" +
20+
"source => \"http://example.com/path/to/hello\"\n" +
1821
"}";
1922
StreamTokenizer tokens = new StreamTokenizer(new StringReader(data));
2023
tokens.quoteChar('"');
@@ -33,6 +36,17 @@ public void testGetPath() throws Exception {
3336

3437
@Test
3538
public void testGetUpdatePath() throws Exception {
36-
assertThat(config.getUpdatePath().toString()).isEqualTo("http://host.com/path/to/hello");
39+
assertThat(config.getUpdatePath().toString()).isEqualTo("http://example.com/path/to/hello");
40+
}
41+
42+
@Test
43+
public void testApply() throws Exception {
44+
FileConfig configMock = mock(FileConfig.class);
45+
when(configMock.getPath()).thenReturn(config.getPath());
46+
when(configMock.getUpdatePath()).thenReturn(config.getUpdatePath());
47+
when(configMock.openStream()).thenReturn(new ByteArrayInputStream("hello world".getBytes()));
48+
configMock.apply();
49+
verify(configMock, atLeastOnce()).apply();
50+
assertThat(configMock.getPath()).hasContent("hello world");
3751
}
3852
}

0 commit comments

Comments
 (0)