Skip to content

Commit 111d668

Browse files
author
Andrew Snare
committed
Don't hold the underlying file/channel open for longer than necessary.
When loading from a database, once read and/or mapped the file/channel can be safely closed. (Even when using MEMORY_MAPPED, once we have the mapped buffer the original file can be safely closed without affecting the buffer.)
1 parent 0b91040 commit 111d668

3 files changed

Lines changed: 45 additions & 53 deletions

File tree

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.maxmind.db;
22

33
import java.io.ByteArrayOutputStream;
4-
import java.io.Closeable;
54
import java.io.File;
65
import java.io.IOException;
76
import java.io.InputStream;
@@ -12,21 +11,33 @@
1211

1312
import com.maxmind.db.Reader.FileMode;
1413

15-
final class BufferHolder implements Closeable {
16-
// DO NOT PASS THESE OUTSIDE THIS CLASS. Doing so will remove thread
17-
// safety.
14+
final class BufferHolder {
15+
// DO NOT PASS OUTSIDE THIS CLASS. Doing so will remove thread safety.
1816
private final ByteBuffer buffer;
19-
private final RandomAccessFile raf;
20-
private final FileChannel fc;
2117

2218
BufferHolder(File database, FileMode mode) throws IOException {
23-
this.raf = new RandomAccessFile(database, "r");
24-
this.fc = this.raf.getChannel();
25-
if (mode == FileMode.MEMORY) {
26-
this.buffer = ByteBuffer.wrap(new byte[(int) this.fc.size()]);
27-
this.fc.read(this.buffer);
28-
} else {
29-
this.buffer = this.fc.map(MapMode.READ_ONLY, 0, this.fc.size());
19+
final RandomAccessFile file = new RandomAccessFile(database, "r");
20+
boolean threw = true;
21+
try {
22+
final FileChannel channel = file.getChannel();
23+
if (mode == FileMode.MEMORY) {
24+
this.buffer = ByteBuffer.wrap(new byte[(int) channel.size()]);
25+
channel.read(this.buffer);
26+
} else {
27+
this.buffer = channel.map(MapMode.READ_ONLY, 0, channel.size());
28+
}
29+
threw = false;
30+
} finally {
31+
try {
32+
// Also closes the underlying channel.
33+
file.close();
34+
} catch (final IOException e) {
35+
// If an exception was underway when we entered the finally block,
36+
// don't stomp over it due to an error closing the file and channel.
37+
if (!threw) {
38+
throw e;
39+
}
40+
}
3041
}
3142
}
3243

@@ -52,15 +63,11 @@ final class BufferHolder implements Closeable {
5263
baos.write(bytes, 0, br);
5364
}
5465
this.buffer = ByteBuffer.wrap(baos.toByteArray());
55-
this.raf = null;
56-
this.fc = null;
5766
}
5867

5968
// This is just to ease unit testing
6069
BufferHolder(ByteBuffer buffer) {
6170
this.buffer = buffer;
62-
this.raf = null;
63-
this.fc = null;
6471
}
6572

6673
/*
@@ -70,14 +77,4 @@ final class BufferHolder implements Closeable {
7077
synchronized ByteBuffer get() {
7178
return this.buffer.duplicate();
7279
}
73-
74-
@Override
75-
public void close() throws IOException {
76-
if (this.fc != null) {
77-
this.fc.close();
78-
}
79-
if (this.raf != null) {
80-
this.raf.close();
81-
}
82-
}
8380
}

src/main/java/com/maxmind/db/Reader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,6 @@ Metadata getMetadata() {
241241
*/
242242
@Override
243243
public void close() throws IOException {
244-
this.bufferHolder.close();
244+
// Nothing to do for now.
245245
}
246246
}

src/test/java/com/maxmind/db/PointerTest.java

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,37 +20,32 @@ public void testWithPointers() throws InvalidDatabaseException,
2020
File file = new File(PointerTest.class.getResource(
2121
"/maxmind-db/test-data/maps-with-pointers.raw").toURI());
2222
BufferHolder ptf = new BufferHolder(file, FileMode.MEMORY);
23-
try {
24-
Decoder decoder = new Decoder(ptf.get(), 0);
23+
Decoder decoder = new Decoder(ptf.get(), 0);
2524

26-
ObjectMapper om = new ObjectMapper();
25+
ObjectMapper om = new ObjectMapper();
2726

28-
ObjectNode map = om.createObjectNode();
29-
map.put("long_key", "long_value1");
30-
assertEquals(map, decoder.decode(0).getNode());
27+
ObjectNode map = om.createObjectNode();
28+
map.put("long_key", "long_value1");
29+
assertEquals(map, decoder.decode(0).getNode());
3130

32-
map = om.createObjectNode();
33-
map.put("long_key", "long_value2");
34-
assertEquals(map, decoder.decode(22).getNode());
31+
map = om.createObjectNode();
32+
map.put("long_key", "long_value2");
33+
assertEquals(map, decoder.decode(22).getNode());
3534

36-
map = om.createObjectNode();
37-
map.put("long_key2", "long_value1");
38-
assertEquals(map, decoder.decode(37).getNode());
35+
map = om.createObjectNode();
36+
map.put("long_key2", "long_value1");
37+
assertEquals(map, decoder.decode(37).getNode());
3938

40-
map = om.createObjectNode();
41-
map.put("long_key2", "long_value2");
42-
assertEquals(map, decoder.decode(50).getNode());
39+
map = om.createObjectNode();
40+
map.put("long_key2", "long_value2");
41+
assertEquals(map, decoder.decode(50).getNode());
4342

44-
map = om.createObjectNode();
45-
map.put("long_key", "long_value1");
46-
assertEquals(map, decoder.decode(55).getNode());
47-
48-
map = om.createObjectNode();
49-
map.put("long_key2", "long_value2");
50-
assertEquals(map, decoder.decode(57).getNode());
51-
} finally {
52-
ptf.close();
53-
}
43+
map = om.createObjectNode();
44+
map.put("long_key", "long_value1");
45+
assertEquals(map, decoder.decode(55).getNode());
5446

47+
map = om.createObjectNode();
48+
map.put("long_key2", "long_value2");
49+
assertEquals(map, decoder.decode(57).getNode());
5550
}
5651
}

0 commit comments

Comments
 (0)