• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Jsondb / jsondb-core / e70db7a9-0b5c-4b3e-bcf3-97387c836ba7

24 Jun 2026 09:15PM UTC coverage: 81.262% (+0.7%) from 80.602%
e70db7a9-0b5c-4b3e-bcf3-97387c836ba7

push

circleci

FarooqKhan
Implement zip-based backup() and restore() for JsonDB collections.

Add JsonDbArchive for creating and reading zip backups of collection JSON
files, with replace and merge restore modes plus 13 unit tests.

585 of 700 branches covered (83.57%)

170 of 196 new or added lines in 2 files covered. (86.73%)

1726 of 2124 relevant lines covered (81.26%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.74
/src/main/java/io/jsondb/io/JsonDbArchive.java
1
/*
2
 * Copyright (c) 2016 Farooq Khan
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
 */
21
package io.jsondb.io;
22

23
import java.io.BufferedInputStream;
24
import java.io.BufferedOutputStream;
25
import java.io.File;
26
import java.io.FileInputStream;
27
import java.io.FileOutputStream;
28
import java.io.IOException;
29
import java.nio.file.Files;
30
import java.nio.file.StandardCopyOption;
31
import java.util.ArrayList;
32
import java.util.LinkedHashMap;
33
import java.util.List;
34
import java.util.Map;
35
import java.util.zip.ZipEntry;
36
import java.util.zip.ZipInputStream;
37
import java.util.zip.ZipOutputStream;
38

39
/**
40
 * Utility for creating and reading JsonDB zip backups.
41
 * Each backup is a zip archive containing collection {@code *.json} files at the root level.
42
 */
43
public final class JsonDbArchive {
44

45
  public static final String DEFAULT_BACKUP_FILENAME = "jsondb-backup.zip";
46

47
  private JsonDbArchive() {
48
  }
49

50
  public static File resolveBackupZipFile(String backupPath) {
51
    if (null == backupPath || backupPath.trim().isEmpty()) {
1!
52
      throw new IllegalArgumentException("Backup path cannot be null or empty");
1✔
53
    }
54
    File path = new File(backupPath);
1✔
55
    if (path.isDirectory()) {
1✔
56
      return new File(path, DEFAULT_BACKUP_FILENAME);
1✔
57
    }
58
    if (!backupPath.toLowerCase().endsWith(".zip")) {
1✔
59
      return new File(backupPath + ".zip");
1✔
60
    }
61
    return path;
1✔
62
  }
63

64
  public static File resolveRestoreZipFile(String restorePath) {
65
    if (null == restorePath || restorePath.trim().isEmpty()) {
1!
NEW
66
      throw new IllegalArgumentException("Restore path cannot be null or empty");
×
67
    }
68
    File path = new File(restorePath);
1✔
69
    if (path.isDirectory()) {
1!
NEW
70
      path = new File(path, DEFAULT_BACKUP_FILENAME);
×
71
    } else if (!restorePath.toLowerCase().endsWith(".zip")) {
1!
NEW
72
      path = new File(restorePath + ".zip");
×
73
    }
74
    return path;
1✔
75
  }
76

77
  public static List<File> listCollectionJsonFiles(File dbDirectory) {
78
    List<File> files = new ArrayList<File>();
1✔
79
    File[] listed = dbDirectory.listFiles();
1✔
80
    if (null != listed) {
1!
81
      for (File file : listed) {
1✔
82
        if (file.isFile() && file.getName().endsWith(".json")) {
1!
83
          files.add(file);
1✔
84
        }
85
      }
86
    }
87
    return files;
1✔
88
  }
89

90
  public static void createBackupZip(File dbDirectory, File zipFile) throws IOException {
91
    File parent = zipFile.getParentFile();
1✔
92
    if (null != parent) {
1!
93
      parent.mkdirs();
1✔
94
    }
95
    List<File> collectionFiles = listCollectionJsonFiles(dbDirectory);
1✔
96
    ZipOutputStream zos = null;
1✔
97
    try {
98
      zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
1✔
99
      byte[] buffer = new byte[8192];
1✔
100
      for (File collectionFile : collectionFiles) {
1✔
101
        ZipEntry entry = new ZipEntry(collectionFile.getName());
1✔
102
        zos.putNextEntry(entry);
1✔
103
        FileInputStream fis = null;
1✔
104
        try {
105
          fis = new FileInputStream(collectionFile);
1✔
106
          int read = 0;
1✔
107
          while ((read = fis.read(buffer)) != -1) {
1✔
108
            zos.write(buffer, 0, read);
1✔
109
          }
110
        } finally {
111
          if (null != fis) {
1!
112
            fis.close();
1✔
113
          }
114
        }
115
        zos.closeEntry();
1✔
116
      }
1✔
117
    } finally {
118
      if (null != zos) {
1!
119
        zos.close();
1✔
120
      }
121
    }
122
  }
1✔
123

124
  public static void extractReplaceCollectionFiles(File dbDirectory, File zipFile) throws IOException {
125
    deleteCollectionJsonFiles(dbDirectory);
1✔
126
    extractCollectionFiles(dbDirectory, zipFile);
1✔
127
  }
1✔
128

129
  public static Map<String, File> extractToDirectory(File targetDirectory, File zipFile) throws IOException {
130
    targetDirectory.mkdirs();
1✔
131
    Map<String, File> extracted = new LinkedHashMap<String, File>();
1✔
132
    ZipInputStream zis = null;
1✔
133
    try {
134
      zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
1✔
135
      ZipEntry entry = null;
1✔
136
      while ((entry = zis.getNextEntry()) != null) {
1✔
137
        if (entry.isDirectory()) {
1!
NEW
138
          continue;
×
139
        }
140
        String entryName = entry.getName();
1✔
141
        if (!isSafeCollectionEntry(entryName)) {
1!
NEW
142
          continue;
×
143
        }
144
        File outputFile = new File(targetDirectory, entryName);
1✔
145
        FileOutputStream fos = null;
1✔
146
        try {
147
          fos = new FileOutputStream(outputFile);
1✔
148
          byte[] buffer = new byte[8192];
1✔
149
          int read = 0;
1✔
150
          while ((read = zis.read(buffer)) != -1) {
1✔
151
            fos.write(buffer, 0, read);
1✔
152
          }
153
        } finally {
154
          if (null != fos) {
1!
155
            fos.close();
1✔
156
          }
157
        }
158
        String collectionName = entryName.substring(0, entryName.length() - ".json".length());
1✔
159
        extracted.put(collectionName, outputFile);
1✔
160
        zis.closeEntry();
1✔
161
      }
1✔
162
    } finally {
163
      if (null != zis) {
1!
164
        zis.close();
1✔
165
      }
166
    }
167
    return extracted;
1✔
168
  }
169

170
  public static void deleteCollectionJsonFiles(File dbDirectory) throws IOException {
171
    for (File file : listCollectionJsonFiles(dbDirectory)) {
1✔
172
      Files.deleteIfExists(file.toPath());
1✔
173
    }
1✔
174
  }
1✔
175

176
  public static String collectionNameFromFile(File collectionFile) {
NEW
177
    String name = collectionFile.getName();
×
NEW
178
    return name.substring(0, name.length() - ".json".length());
×
179
  }
180

181
  private static void extractCollectionFiles(File dbDirectory, File zipFile) throws IOException {
182
    ZipInputStream zis = null;
1✔
183
    try {
184
      zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
1✔
185
      ZipEntry entry = null;
1✔
186
      while ((entry = zis.getNextEntry()) != null) {
1✔
187
        if (entry.isDirectory()) {
1!
NEW
188
          continue;
×
189
        }
190
        String entryName = entry.getName();
1✔
191
        if (!isSafeCollectionEntry(entryName)) {
1!
NEW
192
          continue;
×
193
        }
194
        File outputFile = new File(dbDirectory, entryName);
1✔
195
        FileOutputStream fos = null;
1✔
196
        try {
197
          fos = new FileOutputStream(outputFile);
1✔
198
          byte[] buffer = new byte[8192];
1✔
199
          int read = 0;
1✔
200
          while ((read = zis.read(buffer)) != -1) {
1✔
201
            fos.write(buffer, 0, read);
1✔
202
          }
203
        } finally {
204
          if (null != fos) {
1!
205
            fos.close();
1✔
206
          }
207
        }
208
        zis.closeEntry();
1✔
209
      }
1✔
210
    } finally {
211
      if (null != zis) {
1!
212
        zis.close();
1✔
213
      }
214
    }
215
  }
1✔
216

217
  public static boolean isSafeCollectionEntry(String entryName) {
218
    if (null == entryName || entryName.contains("..") || entryName.contains("/") || entryName.contains("\\")) {
1!
219
      return false;
1✔
220
    }
221
    return entryName.endsWith(".json");
1✔
222
  }
223

224
  public static void copyCollectionFile(File source, File target) throws IOException {
225
    Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
1✔
226
  }
1✔
227
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc