Skip to content

Commit 8e011b2

Browse files
committed
implement backup service
1 parent f7e59c8 commit 8e011b2

File tree

3 files changed

+322
-143
lines changed

3 files changed

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

0 commit comments

Comments
 (0)