Skip to content

Commit d7910ba

Browse files
authored
Backup
2 parents f7e59c8 + fbb024f commit d7910ba

File tree

4 files changed

+247
-135
lines changed

4 files changed

+247
-135
lines changed
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
package com.acuity.iot.dsa.dslink.sys.backup;
2+
3+
import java.io.File;
4+
import java.io.FileInputStream;
5+
import java.io.FileOutputStream;
6+
import java.io.FilenameFilter;
7+
import java.io.IOException;
8+
import java.io.InputStream;
9+
import java.util.Arrays;
10+
import java.util.Calendar;
11+
import java.util.zip.ZipEntry;
12+
import java.util.zip.ZipOutputStream;
13+
import org.iot.dsa.DSRuntime;
14+
import org.iot.dsa.DSRuntime.Timer;
15+
import org.iot.dsa.dslink.DSLink;
16+
import org.iot.dsa.io.NodeEncoder;
17+
import org.iot.dsa.io.json.JsonWriter;
18+
import org.iot.dsa.node.DSIValue;
19+
import org.iot.dsa.node.DSInfo;
20+
import org.iot.dsa.node.DSLong;
21+
import org.iot.dsa.node.DSNode;
22+
import org.iot.dsa.node.action.ActionInvocation;
23+
import org.iot.dsa.node.action.ActionResult;
24+
import org.iot.dsa.node.action.DSAction;
25+
import org.iot.dsa.time.DSTime;
26+
27+
public class SysBackupService extends DSNode implements Runnable {
28+
29+
static final String INTERVAL = "Backup Interval";
30+
static final String MAXIMUM = "Max Number of Backups";
31+
static final String SAVE = "Save";
32+
33+
private DSInfo interval = getInfo(INTERVAL);
34+
private DSInfo maximum = getInfo(MAXIMUM);
35+
private DSInfo save = getInfo(SAVE);
36+
37+
private DSLink link;
38+
private Timer nextSave;
39+
private Object lock = new Object();
40+
41+
@Override
42+
protected void declareDefaults() {
43+
declareDefault(SAVE, DSAction.DEFAULT);
44+
declareDefault(INTERVAL, DSLong.valueOf(60));
45+
declareDefault(MAXIMUM, DSLong.valueOf(3));
46+
}
47+
48+
@Override
49+
protected void onStable() {
50+
File nodes = getLink().getConfig().getNodesFile();
51+
if (nodes.exists()) {
52+
synchronized (lock) {
53+
scheduleNextSave();
54+
}
55+
} else {
56+
DSRuntime.run(this);
57+
}
58+
}
59+
60+
private DSLink getLink() {
61+
if (link == null) {
62+
link = (DSLink) getAncestor(DSLink.class);
63+
}
64+
return link;
65+
}
66+
67+
@Override
68+
public ActionResult onInvoke(DSInfo action, ActionInvocation invocation) {
69+
if (action == save) {
70+
save();
71+
} else {
72+
super.onInvoke(action, invocation);
73+
}
74+
return null;
75+
}
76+
77+
@Override
78+
public void onChildChanged(DSInfo info) {
79+
super.onChildChanged(info);
80+
if (info == interval) {
81+
DSIValue value = info.getValue();
82+
synchronized (lock) {
83+
if (nextSave != null) {
84+
long newNextRun = (value.toElement().toLong() * 60000) + System.currentTimeMillis();
85+
long scheduledNextRun = nextSave.nextRun();
86+
if (newNextRun < scheduledNextRun) {
87+
nextSave.cancel();
88+
nextSave = DSRuntime.runAt(this, newNextRun);
89+
}
90+
}
91+
}
92+
}
93+
}
94+
95+
/**
96+
* Serializes the configuration database.
97+
*/
98+
public void save() {
99+
if (!getLink().isSaveEnabled()) {
100+
return;
101+
}
102+
ZipOutputStream zos = null;
103+
InputStream in = null;
104+
try {
105+
File nodes = getLink().getConfig().getNodesFile();
106+
String name = nodes.getName();
107+
if (nodes.exists()) {
108+
info("Backing up the node database...");
109+
StringBuilder buf = new StringBuilder();
110+
Calendar cal = DSTime.getCalendar(System.currentTimeMillis());
111+
if (name.endsWith(".zip")) {
112+
String tmp = name.substring(0, name.lastIndexOf(".zip"));
113+
buf.append(tmp).append('.');
114+
DSTime.encodeForFiles(cal, buf);
115+
buf.append(".zip");
116+
File bakFile = new File(nodes.getParent(), buf.toString());
117+
nodes.renameTo(bakFile);
118+
} else {
119+
buf.append(name).append('.');
120+
DSTime.encodeForFiles(cal, buf);
121+
buf.append(".zip");
122+
File back = new File(nodes.getParent(), buf.toString());
123+
FileOutputStream fos = new FileOutputStream(back);
124+
zos = new ZipOutputStream(fos);
125+
zos.putNextEntry(new ZipEntry(nodes.getName()));
126+
byte[] b = new byte[4096];
127+
in = new FileInputStream(nodes);
128+
int len = in.read(b);
129+
while (len > 0) {
130+
zos.write(b, 0, len);
131+
len = in.read(b);
132+
}
133+
in.close();
134+
in = null;
135+
zos.closeEntry();
136+
zos.close();
137+
zos = null;
138+
}
139+
DSTime.recycle(cal);
140+
}
141+
long time = System.currentTimeMillis();
142+
info("Saving node database " + nodes.getAbsolutePath());
143+
JsonWriter writer = null;
144+
if (name.endsWith(".zip")) {
145+
String tmp = name.substring(0, name.lastIndexOf(".zip"));
146+
writer = new JsonWriter(nodes, tmp + ".json");
147+
} else {
148+
writer = new JsonWriter(nodes);
149+
}
150+
NodeEncoder.encode(writer, getLink());
151+
writer.close();
152+
trimBackups();
153+
time = System.currentTimeMillis() - time;
154+
info("Node database saved: " + time + "ms");
155+
} catch (Exception x) {
156+
error("Saving node database", x);
157+
}
158+
try {
159+
if (in != null) {
160+
in.close();
161+
}
162+
} catch (IOException x) {
163+
error("Closing input", x);
164+
}
165+
try {
166+
if (zos != null) {
167+
zos.close();
168+
}
169+
} catch (IOException x) {
170+
error("Closing output", x);
171+
}
172+
}
173+
174+
/**
175+
* Called by save, no need to explicitly call.
176+
*/
177+
private void trimBackups() {
178+
final File nodes = getLink().getConfig().getNodesFile();
179+
if (nodes == null) {
180+
return;
181+
}
182+
final String nodesName = nodes.getName();
183+
final boolean isZip = nodesName.endsWith(".zip");
184+
int idx = nodesName.lastIndexOf('.');
185+
final String nameBase = nodesName.substring(0, idx);
186+
File dir = nodes.getAbsoluteFile().getParentFile();
187+
File[] backups = dir.listFiles(new FilenameFilter() {
188+
public boolean accept(File dir, String name) {
189+
if (name.equals(nodesName)) {
190+
return false;
191+
}
192+
if (isZip) {
193+
if (name.endsWith(".zip")) {
194+
return name.startsWith(nameBase);
195+
}
196+
} else {
197+
if (name.endsWith(".json")) {
198+
return name.startsWith(nameBase);
199+
}
200+
}
201+
return false;
202+
}
203+
});
204+
if (backups == null) {
205+
return;
206+
}
207+
Arrays.sort(backups);
208+
int maxBackups = maximum.getElement().toInt();
209+
if (backups.length <= maxBackups) {
210+
return;
211+
}
212+
for (int i = 0, len = backups.length - maxBackups; i < len; i++) {
213+
backups[i].delete();
214+
}
215+
}
216+
217+
private void scheduleNextSave() {
218+
long saveInterval = interval.getElement().toLong();
219+
saveInterval *= 60000;
220+
nextSave = DSRuntime.runDelayed(this, saveInterval);
221+
}
222+
223+
@Override
224+
public void run() {
225+
synchronized(lock) {
226+
save();
227+
scheduleNextSave();
228+
}
229+
}
230+
231+
@Override
232+
public void onStopped() {
233+
if (nextSave != null) {
234+
nextSave.cancel();
235+
nextSave = null;
236+
}
237+
save();
238+
}
239+
240+
}

0 commit comments

Comments
 (0)