Skip to content

Commit 560ad96

Browse files
authored
Merge pull request #330 from scijava/fix-raf-creation
Fix #329: Empty RAF creation on read
2 parents 0a6709e + 15b70a1 commit 560ad96

File tree

2 files changed

+106
-42
lines changed

2 files changed

+106
-42
lines changed

src/main/java/org/scijava/io/handle/FileHandle.java

Lines changed: 80 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@ public class FileHandle extends AbstractDataHandle<FileLocation> {
6060

6161
// -- FileHandle methods --
6262

63-
/** Gets the random access file object backing this FileHandle. */
63+
/**
64+
* Gets the random access file object backing this FileHandle. If the
65+
* underlying file does not exist yet, it will be created.
66+
*/
6467
public RandomAccessFile getRandomAccessFile() throws IOException {
65-
return raf();
68+
return writer();
6669
}
6770

6871
public String getMode() {
@@ -101,199 +104,199 @@ public Date lastModified() {
101104

102105
@Override
103106
public long offset() throws IOException {
104-
return raf().getFilePointer();
107+
return exists() ? reader().getFilePointer() : 0;
105108
}
106109

107110
@Override
108111
public long length() throws IOException {
109-
return exists() ? raf().length() : -1;
112+
return exists() ? reader().length() : -1;
110113
}
111114

112115
@Override
113116
public void setLength(final long length) throws IOException {
114-
raf().setLength(length);
117+
writer().setLength(length);
115118
}
116119

117120
@Override
118121
public int read() throws IOException {
119-
return raf().read();
122+
return reader().read();
120123
}
121124

122125
@Override
123126
public int read(final byte[] b) throws IOException {
124-
return raf().read(b);
127+
return reader().read(b);
125128
}
126129

127130
@Override
128131
public int read(final byte[] b, final int off, final int len)
129132
throws IOException
130133
{
131-
return raf().read(b, off, len);
134+
return reader().read(b, off, len);
132135
}
133136

134137
@Override
135138
public void seek(final long pos) throws IOException {
136-
raf().seek(pos);
139+
reader().seek(pos);
137140
}
138141

139142
// -- DataInput methods --
140143

141144
@Override
142145
public boolean readBoolean() throws IOException {
143-
return raf().readBoolean();
146+
return reader().readBoolean();
144147
}
145148

146149
@Override
147150
public byte readByte() throws IOException {
148-
return raf().readByte();
151+
return reader().readByte();
149152
}
150153

151154
@Override
152155
public char readChar() throws IOException {
153-
return raf().readChar();
156+
return reader().readChar();
154157
}
155158

156159
@Override
157160
public double readDouble() throws IOException {
158-
return raf().readDouble();
161+
return reader().readDouble();
159162
}
160163

161164
@Override
162165
public float readFloat() throws IOException {
163-
return raf().readFloat();
166+
return reader().readFloat();
164167
}
165168

166169
@Override
167170
public void readFully(final byte[] b) throws IOException {
168-
raf().readFully(b);
171+
reader().readFully(b);
169172
}
170173

171174
@Override
172175
public void readFully(final byte[] b, final int off, final int len)
173176
throws IOException
174177
{
175-
raf().readFully(b, off, len);
178+
reader().readFully(b, off, len);
176179
}
177180

178181
@Override
179182
public int readInt() throws IOException {
180-
return raf().readInt();
183+
return reader().readInt();
181184
}
182185

183186
@Override
184187
public String readLine() throws IOException {
185-
return raf().readLine();
188+
return reader().readLine();
186189
}
187190

188191
@Override
189192
public long readLong() throws IOException {
190-
return raf().readLong();
193+
return reader().readLong();
191194
}
192195

193196
@Override
194197
public short readShort() throws IOException {
195-
return raf().readShort();
198+
return reader().readShort();
196199
}
197200

198201
@Override
199202
public int readUnsignedByte() throws IOException {
200-
return raf().readUnsignedByte();
203+
return reader().readUnsignedByte();
201204
}
202205

203206
@Override
204207
public int readUnsignedShort() throws IOException {
205-
return raf().readUnsignedShort();
208+
return reader().readUnsignedShort();
206209
}
207210

208211
@Override
209212
public String readUTF() throws IOException {
210-
return raf().readUTF();
213+
return reader().readUTF();
211214
}
212215

213216
@Override
214217
public int skipBytes(final int n) throws IOException {
215-
return raf().skipBytes(n);
218+
return reader().skipBytes(n);
216219
}
217220

218221
// -- DataOutput methods --
219222

220223
@Override
221224
public void write(final byte[] b) throws IOException {
222-
raf().write(b);
225+
writer().write(b);
223226
}
224227

225228
@Override
226229
public void write(final byte[] b, final int off, final int len)
227230
throws IOException
228231
{
229-
raf().write(b, off, len);
232+
writer().write(b, off, len);
230233
}
231234

232235
@Override
233236
public void write(final int b) throws IOException {
234-
raf().write(b);
237+
writer().write(b);
235238
}
236239

237240
@Override
238241
public void writeBoolean(final boolean v) throws IOException {
239-
raf().writeBoolean(v);
242+
writer().writeBoolean(v);
240243
}
241244

242245
@Override
243246
public void writeByte(final int v) throws IOException {
244-
raf().writeByte(v);
247+
writer().writeByte(v);
245248
}
246249

247250
@Override
248251
public void writeBytes(final String s) throws IOException {
249-
raf().writeBytes(s);
252+
writer().writeBytes(s);
250253
}
251254

252255
@Override
253256
public void writeChar(final int v) throws IOException {
254-
raf().writeChar(v);
257+
writer().writeChar(v);
255258
}
256259

257260
@Override
258261
public void writeChars(final String s) throws IOException {
259-
raf().writeChars(s);
262+
writer().writeChars(s);
260263
}
261264

262265
@Override
263266
public void writeDouble(final double v) throws IOException {
264-
raf().writeDouble(v);
267+
writer().writeDouble(v);
265268
}
266269

267270
@Override
268271
public void writeFloat(final float v) throws IOException {
269-
raf().writeFloat(v);
272+
writer().writeFloat(v);
270273
}
271274

272275
@Override
273276
public void writeInt(final int v) throws IOException {
274-
raf().writeInt(v);
277+
writer().writeInt(v);
275278
}
276279

277280
@Override
278281
public void writeLong(final long v) throws IOException {
279-
raf().writeLong(v);
282+
writer().writeLong(v);
280283
}
281284

282285
@Override
283286
public void writeShort(final int v) throws IOException {
284-
raf().writeShort(v);
287+
writer().writeShort(v);
285288
}
286289

287290
@Override
288291
public void writeUTF(final String str) throws IOException {
289-
raf().writeUTF(str);
292+
writer().writeUTF(str);
290293
}
291294

292295
// -- Closeable methods --
293296

294297
@Override
295298
public synchronized void close() throws IOException {
296-
if (raf != null) raf().close();
299+
if (raf != null) raf.close();
297300
closed = true;
298301
}
299302

@@ -306,12 +309,47 @@ public Class<FileLocation> getType() {
306309

307310
// -- Helper methods --
308311

309-
private RandomAccessFile raf() throws IOException {
310-
if (raf == null) initRAF();
312+
/**
313+
* Access method for the internal {@link RandomAccessFile}, that succeeds
314+
* independently of the underlying file existing on disk. This allows us to
315+
* create a new file for writing.
316+
*
317+
* @return the internal {@link RandomAccessFile} creating a new file on disk
318+
* if needed.
319+
* @throws IOException if the {@link RandomAccessFile} could not be created.
320+
*/
321+
private RandomAccessFile writer() throws IOException {
322+
if (raf == null) initRAF(true);
311323
return raf;
312324
}
313325

314-
private synchronized void initRAF() throws IOException {
326+
/**
327+
* Access method for the internal {@link RandomAccessFile}, that only succeeds
328+
* if the underlying file exists on disk. This prevents accidental creation of
329+
* an empty file when calling read operations on a non-existent file.
330+
*
331+
* @return the internal {@link RandomAccessFile}.
332+
* @throws IOException if the {@link RandomAccessFile} could not be created,
333+
* or the backing file does not exists.
334+
*/
335+
private RandomAccessFile reader() throws IOException {
336+
if (raf == null) initRAF(false);
337+
return raf;
338+
}
339+
340+
/**
341+
* Initializes the {@link RandomAccessFile}.
342+
*
343+
* @param create whether to create the {@link RandomAccessFile} if the
344+
* underlying file does not exist yet.
345+
* @throws IOException if the {@link RandomAccessFile} could not be created,
346+
* or the backing file does not exist and the {@code create}
347+
* parameter was set to {@code false}.
348+
*/
349+
private synchronized void initRAF(final boolean create) throws IOException {
350+
if (!create && !exists()) {
351+
throw new IOException("Trying to read from non-existent file!");
352+
}
315353
if (closed) throw new IOException("Handle already closed");
316354
if (raf != null) return;
317355
raf = new RandomAccessFile(get().getFile(), getMode());

src/test/java/org/scijava/io/handle/FileHandleTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@
3535
import static org.junit.Assert.assertEquals;
3636
import static org.junit.Assert.assertFalse;
3737
import static org.junit.Assert.assertTrue;
38+
import static org.junit.Assert.fail;
3839

3940
import java.io.File;
4041
import java.io.FileOutputStream;
4142
import java.io.IOException;
43+
import java.net.URI;
4244

4345
import org.junit.Test;
4446
import org.scijava.Context;
@@ -108,4 +110,28 @@ public void testNotCreatedByClose() throws IOException {
108110
handle.close();
109111
assertFalse(nonExistentFile.exists());
110112
}
113+
114+
@Test
115+
public void testNotCreatedByRead() throws IOException {
116+
final Context ctx = new Context();
117+
final DataHandleService dhs = ctx.service(DataHandleService.class);
118+
119+
final File nonExistentFile = //
120+
File.createTempFile("FileHandleTest", "none xistent file");
121+
final URI uri = nonExistentFile.toURI();
122+
assertTrue(nonExistentFile.delete());
123+
assertFalse(nonExistentFile.exists());
124+
125+
final FileLocation loc = new FileLocation(uri);
126+
try (final DataHandle<?> handle = dhs.create(loc)) {
127+
assertFalse(handle.exists());
128+
handle.read(); // this will fail as there is no underlying file!
129+
fail("Read successfully from non-existing file!");
130+
}
131+
catch (final IOException exc) {
132+
// should be thrown
133+
}
134+
assertFalse(nonExistentFile.exists());
135+
// reading from the non-existing file should not create it!
136+
}
111137
}

0 commit comments

Comments
 (0)