Skip to content

Commit e8ebbc7

Browse files
committed
Fix NPE in ParseKeyValueCache due to cache dir not exist
1 parent d1e2907 commit e8ebbc7

File tree

2 files changed

+79
-29
lines changed

2 files changed

+79
-29
lines changed

Parse/src/main/java/com/parse/ParseKeyValueCache.java

+42-28
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,21 @@
6464
}
6565

6666
private static File getKeyValueCacheDir() {
67+
if (directory == null || !directory.exists()) {
68+
directory.mkdir();
69+
}
6770
return directory;
6871
}
6972

7073
/**
7174
* How many files are in the key-value cache.
7275
*/
7376
/* package */ static int size() {
74-
return getKeyValueCacheDir().listFiles().length;
77+
File[] files = getKeyValueCacheDir().listFiles();
78+
if (files == null) {
79+
return 0;
80+
}
81+
return files.length;
7582
}
7683

7784
private static File getKeyValueCacheFile(String key) {
@@ -96,7 +103,7 @@ private static long getKeyValueCacheAge(File cacheFile) {
96103
}
97104
}
98105

99-
/* package */ private static File createKeyValueCacheFile(String key) {
106+
private static File createKeyValueCacheFile(String key) {
100107
String filename = String.valueOf(new Date().getTime()) + '.' + key;
101108
return new File(getKeyValueCacheDir(), filename);
102109
}
@@ -127,9 +134,7 @@ private static long getKeyValueCacheAge(File cacheFile) {
127134
}
128135
File f = createKeyValueCacheFile(key);
129136
try {
130-
FileOutputStream out = new FileOutputStream(f);
131-
out.write(value.getBytes("UTF-8"));
132-
out.close();
137+
ParseFileUtils.writeByteArrayToFile(f, value.getBytes("UTF-8"));
133138
} catch (UnsupportedEncodingException e) {
134139
// do nothing
135140
} catch (IOException e) {
@@ -138,38 +143,47 @@ private static long getKeyValueCacheAge(File cacheFile) {
138143

139144
// Check if we should kick out old cache entries
140145
File[] files = getKeyValueCacheDir().listFiles();
146+
// We still need this check since dir.mkdir() may fail
147+
if (files == null || files.length == 0) {
148+
return;
149+
}
150+
141151
int numFiles = files.length;
142152
int numBytes = 0;
143153
for (File file : files) {
144154
numBytes += file.length();
145155
}
146-
if (numFiles > maxKeyValueCacheFiles || numBytes > maxKeyValueCacheBytes) {
147-
// We need to kick out some cache entries.
148-
// Sort oldest-first. We touch on read so mtime is really LRU.
149-
// Sometimes (i.e. tests) the time of lastModified isn't granular enough,
150-
// so we resort
151-
// to sorting by the file name which is always prepended with time in ms
152-
Arrays.sort(files, new Comparator<File>() {
153-
@Override
154-
public int compare(File f1, File f2) {
155-
int dateCompare = Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
156-
if (dateCompare != 0) {
157-
return dateCompare;
158-
} else {
159-
return f1.getName().compareTo(f2.getName());
160-
}
161-
}
162-
});
163156

164-
for (File file : files) {
165-
numFiles--;
166-
numBytes -= file.length();
167-
file.delete();
157+
// If we do not need to clear the cache, simply return
158+
if (numFiles <= maxKeyValueCacheFiles && numBytes <= maxKeyValueCacheBytes) {
159+
return;
160+
}
168161

169-
if (numFiles <= maxKeyValueCacheFiles && numBytes <= maxKeyValueCacheBytes) {
170-
break;
162+
// We need to kick out some cache entries.
163+
// Sort oldest-first. We touch on read so mtime is really LRU.
164+
// Sometimes (i.e. tests) the time of lastModified isn't granular enough,
165+
// so we resort
166+
// to sorting by the file name which is always prepended with time in ms
167+
Arrays.sort(files, new Comparator<File>() {
168+
@Override
169+
public int compare(File f1, File f2) {
170+
int dateCompare = Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
171+
if (dateCompare != 0) {
172+
return dateCompare;
173+
} else {
174+
return f1.getName().compareTo(f2.getName());
171175
}
172176
}
177+
});
178+
179+
for (File file : files) {
180+
numFiles--;
181+
numBytes -= file.length();
182+
file.delete();
183+
184+
if (numFiles <= maxKeyValueCacheFiles && numBytes <= maxKeyValueCacheBytes) {
185+
break;
186+
}
173187
}
174188
}
175189
}

Parse/src/test/java/com/parse/ParseKeyValueCacheTest.java

+37-1
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,29 @@
1414
import org.junit.Test;
1515
import org.junit.rules.TemporaryFolder;
1616

17+
import java.io.File;
1718
import java.util.ArrayList;
1819
import java.util.List;
1920
import java.util.concurrent.Callable;
2021

2122
import bolts.Task;
2223

24+
import static org.junit.Assert.assertArrayEquals;
25+
import static org.junit.Assert.assertEquals;
26+
import static org.junit.Assert.assertFalse;
27+
import static org.junit.Assert.assertTrue;
28+
2329
public class ParseKeyValueCacheTest {
2430

31+
private File keyValueCacheDir;
32+
2533
@Rule
2634
public TemporaryFolder temporaryFolder = new TemporaryFolder();
2735

2836
@Before
2937
public void setUp() throws Exception {
30-
ParseKeyValueCache.initialize(temporaryFolder.newFolder("ParseKeyValueCache"));
38+
keyValueCacheDir = temporaryFolder.newFolder("ParseKeyValueCache");
39+
ParseKeyValueCache.initialize(keyValueCacheDir);
3140
}
3241

3342
@After
@@ -59,4 +68,31 @@ public Void call() throws Exception {
5968
}
6069
ParseTaskUtils.wait(Task.whenAll(tasks));
6170
}
71+
72+
@Test
73+
public void testSaveToKeyValueCacheWithoutCacheDir() throws Exception {
74+
// Delete the cache folder(Simulate users clear the app cache)
75+
assertTrue(keyValueCacheDir.exists());
76+
keyValueCacheDir.delete();
77+
assertFalse(keyValueCacheDir.exists());
78+
79+
// Save a key value pair
80+
ParseKeyValueCache.saveToKeyValueCache("key", "value");
81+
82+
// Verify cache file is correct
83+
assertEquals(1, keyValueCacheDir.listFiles().length);
84+
assertArrayEquals(
85+
"value".getBytes(), ParseFileUtils.readFileToByteArray(keyValueCacheDir.listFiles()[0]));
86+
}
87+
88+
@Test
89+
public void testGetSizeWithoutCacheDir() throws Exception {
90+
// Delete the cache folder(Simulate users clear the app cache)
91+
assertTrue(keyValueCacheDir.exists());
92+
keyValueCacheDir.delete();
93+
assertFalse(keyValueCacheDir.exists());
94+
95+
// Verify size is zero
96+
assertEquals(0, ParseKeyValueCache.size());
97+
}
6298
}

0 commit comments

Comments
 (0)