9

There are many examples on the internet showing how to use StandardOpenOption.DELETE_ON_CLOSE, such as this:

Files.write(myTempFile, ..., StandardOpenOption.DELETE_ON_CLOSE);

Other examples similarly use Files.newOutputStream(..., StandardOpenOption.DELETE_ON_CLOSE).

I suspect all of these examples are probably flawed. The purpose of writing a file is that you're going to read it back at some point; otherwise, why bother writing it? But wouldn't DELETE_ON_CLOSE cause the file to be deleted before you have a chance to read it?

If you create a work file (to work with large amounts of data that are too large to keep in memory) then wouldn't you use RandomAccessFile instead, which allows both read and write access? However, RandomAccessFile doesn't give you the option to specify DELETE_ON_CLOSE, as far as I can see.

So can someone show me how DELETE_ON_CLOSE is actually useful?

Klitos Kyriacou
  • 10,634
  • 2
  • 38
  • 70
  • 2
    I've recently been wondering the same thing. Bizarrely, [the Oracle documentation for "Mapping java.io.File Functionality to java.nio.file"](https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping) suggests using `DELETE_ON_CLOSE` instead of the old `File.deleteOnExit` method, but they are not the same thing! – daiscog Dec 18 '15 at 10:49
  • 1
    Only example I can think of is if you have to test if a write of a certain size of a file will work out or not. I don't see that tremendously useful though. – eis Dec 18 '15 at 10:49
  • 1
    @daiscog The Oracle documentation says `File.deleteOnExit` is "Replaced by the DELETE_ON_CLOSE option specified in the createFile method." That is just plain wrong. The createFile method doesn't take a DELETE_ON_CLOSE parameter. – Klitos Kyriacou Dec 18 '15 at 11:06
  • 1
    @KlitosKyriacou Indeed. Clearly Oracle's quality control of its documentation leaves a lot to be desired. The `File.deleteOnExit` method actually has no suitable replacement in the new `java.nio.file` API, which means moving to the new API in some situations is just not possible. – daiscog Dec 18 '15 at 11:11

3 Answers3

3

First of all I agree with you Files.write(myTempFile, ..., StandardOpenOption.DELETE_ON_CLOSE) in this example the use of DELETE_ON_CLOSE is meaningless. After a (not so intense) search through the internet the only example I could find which shows the usage as mentioned was the one from which you might got it (http://softwarecave.org/2014/02/05/create-temporary-files-and-directories-using-java-nio2/).

This option is not intended to be used for Files.write(...) only. The API make is quite clear:

This option is primarily intended for use with work files that are used solely by a single instance of the Java virtual machine. This option is not recommended for use when opening files that are open concurrently by other entities.

Sorry I can't give you a meaningful short example, but see such file like a swap file/partition used by an operating system. In cases where the current JVM have the need to temporarily store data on the disc and after the shutdown the data are of no use anymore. As practical example I would mention it is similar to an JEE application server which might decide to serialize some entities to disc to freeup memory.

edit Maybe the following (oversimplified code) can be taken as example to demonstrate the principle. (so please: nobody should start a discussion about that this "data management" could be done differently, using fixed temporary filename is bad and so on, ...)

  • in the try-with-resource block you need for some reason to externalize data (the reasons are not subject of the discussion)
  • you have random read/write access to this externalized data
  • this externalized data only is of use only inside the try-with-resource block
  • with the use of the StandardOpenOption.DELETE_ON_CLOSE option you don't need to handle the deletion after the use yourself, the JVM will take care about it (the limitations and edge cases are described in the API)

.

static final int RECORD_LENGTH = 20;
static final String RECORD_FORMAT = "%-" + RECORD_LENGTH + "s";

// add exception handling, left out only for the example
public static void main(String[] args) throws Exception {
    EnumSet<StandardOpenOption> options = EnumSet.of(
            StandardOpenOption.CREATE,
            StandardOpenOption.WRITE,
            StandardOpenOption.READ,
            StandardOpenOption.DELETE_ON_CLOSE
    );

    Path file = Paths.get("/tmp/enternal_data.tmp");
    try (SeekableByteChannel sbc = Files.newByteChannel(file, options)) {

        // during your business processing the below two cases might happen
        // several times in random order

        // example of huge datastructure to externalize
        String[] sampleData = {"some", "huge", "datastructure"};
        for (int i = 0; i < sampleData.length; i++) {
            byte[] buffer = String.format(RECORD_FORMAT, sampleData[i])
                    .getBytes();
            ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
            sbc.position(i * RECORD_LENGTH);
            sbc.write(byteBuffer);
        }

        // example of processing which need the externalized data
        Random random = new Random();
        byte[] buffer = new byte[RECORD_LENGTH];
        ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
        for (int i = 0; i < 10; i++) {
            sbc.position(RECORD_LENGTH * random.nextInt(sampleData.length));
            sbc.read(byteBuffer);
            byteBuffer.flip();
            System.out.printf("loop: %d  %s%n", i, new String(buffer));
        }
    }
}
SubOptimal
  • 22,518
  • 3
  • 53
  • 69
  • I've read that API documentation, too, but it doesn't make much sense to me. The only way I can think of of making DELETE_ON_CLOSE be of any use is when you specify it when opening an OutputStream, and then you open an InputStream on the same path. The file needs to be read *before* the OutputStream is closed (otherwise the file would be deleted). So you then have an OutputStream and an InputStream on the same path at the same time. Wouldn't that go against the API documentation that says "This option is not recommended for use when opening files that are open concurrently by other entities."? – Klitos Kyriacou Dec 18 '15 at 12:07
  • @KlitosKyriacou I think by "other entities" the documentation means independent parts of code. I think having a single part of the application code control both an input and output stream doesn't count. – daiscog Dec 18 '15 at 13:14
  • @KlitosKyriacou As daiscog mention, I believe it is ment to use the came chanel for reading and writing. For me using a separate Output- and InputStream would be a case where it was not tought for. See my added example to demonstrate what I had in mind. – SubOptimal Dec 18 '15 at 13:21
2

The DELETE_ON_CLOSE is intended for working temp files.

If you need to make some operation that needs too be temporaly stored on a file but you don't need to use the file outside of the current execution a DELETE_ON_CLOSE in a good solution for that.

An example is when you need to store informations that can't be mantained in memory for example because they are too heavy. Another example is when you need to store temporarely the informations and you need them only in a second moment and you don't like to occupy memory for that.

Imagine also a situation in which a process needs a lot of time to be completed. You store informations on a file and only later you use them (perhaps many minutes or hours after). This guarantees you that the memory is not used for those informations if you don't need them.

The DELETE_ON_CLOSE try to delete the file when you explicitly close it calling the method close() or when the JVM is shutting down if not manually closed before.

Davide Lorenzo MARINO
  • 26,420
  • 4
  • 39
  • 56
  • Yes, I understand that, but what use would such a file be? – Klitos Kyriacou Dec 18 '15 at 10:43
  • I will add some examples – Davide Lorenzo MARINO Dec 18 '15 at 10:43
  • 3
    The example provided by OP, Files.write(), [states](http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#write%28java.nio.file.Path,%20byte[],%20java.nio.file.OpenOption...%29) that "The method ensures that the file is closed when all bytes have been written (or an I/O error or other runtime exception is thrown)." so I don't see a way of ever reading back what you've written. This makes it not useful even for temporary storage, so I fail to see the point of your answer. – eis Dec 18 '15 at 10:44
  • 1
    Taking your examples: "to store temporarely the informations and you need them only in a second moment" and "You store informations on a file and only later you use them". If you need to use the information you've just written to the file, how do you access that information? The file is write-only. That is the whole point of my question. Can you show some code? – Klitos Kyriacou Dec 18 '15 at 10:54
  • So in your example scenario, you'd need to keep the file open for writing to stop it being deleted throughout that whole long process _even if you no longer need to write to it_ right up until, and also during, the time you read it back again later. The old `File.deleteOnExit` method seems to be much better. – daiscog Dec 18 '15 at 10:55
  • Yes exactly. You need to mantain the file opened until the end of the process – Davide Lorenzo MARINO Dec 18 '15 at 10:57
  • But that seems like a bit of a waste, to keep a resource open like that when it's not needed. – daiscog Dec 18 '15 at 10:58
  • There are also some tricks to use the old deleteOnExit behaviour. I search the link and I'll show you – Davide Lorenzo MARINO Dec 18 '15 at 11:31
  • Well yes, you can do a `path.toFile().deleteOnExit()`, but having to regress to the old API isn't really a desirable solution. Ideally, Path should have a `deleteOnExit()` method itself to avoid the overhead of having to call `toFile()`. Incidentally, I do agree that DELETE_ON_CLOSE is useful for certain situations, but you need to open the file for both reading and writing at the same time. The point the OP was making is that methods like `Files.newOutputStream` do not let you also specify the READ option (according to the docs). – daiscog Dec 18 '15 at 11:46
  • @daiscog you cannot keep the file open for writing when using Files.write(). The method will close the file all by itself (like I quoted), so there is no way to keep the file open -> you cannot use it even for temporary files. – eis Dec 18 '15 at 12:03
  • @eis Just because the option is meaningless in some methods doesn't mean it's meaningless everywhere. See [Files.newByteChannel](http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#newByteChannel(java.nio.file.Path,%20java.nio.file.OpenOption...)) for example. I agree with the OP's point that examples/tutorials which use it in `Files.write` and `Files.newOutputStream` may be flawed, but there are nonetheless valid uses in other methods. Given the actual question asked was "can someone show me how DELETE_ON_CLOSE is actually useful?" an answer quoting a valid use case is correct. – daiscog Dec 18 '15 at 12:52
  • @daiscog fair enough, can agree with this. – eis Dec 19 '15 at 08:52
1

Here are two possible ways it can be used:

1. When calling Files.newByteChannel

This method returns a SeekableByteChannel suitable for both reading and writing, in which the current position can be modified.

Seems quite useful for situations where some data needs to be stored out of memory for read/write access and doesn't need to be persisted after the application closes.

2. Write to a file, read back, delete:

An example using an arbitrary text file:

Path p = Paths.get("C:\\test", "foo.txt");
System.out.println(Files.exists(p));
try {
    Files.createFile(p);
    System.out.println(Files.exists(p));
    try (BufferedWriter out = Files.newBufferedWriter(p, Charset.defaultCharset(), StandardOpenOption.DELETE_ON_CLOSE)) {
        out.append("Hello, World!");
        out.flush();
        try (BufferedReader in = Files.newBufferedReader(p, Charset.defaultCharset())) {
            String line;
            while ((line = in.readLine()) != null) {
                System.out.println(line);
            }
        }
    }
} catch (IOException ex) {
    ex.printStackTrace();
}
System.out.println(Files.exists(p));

This outputs (as expected):

false
true
Hello, World!
false

This example is obviously trivial, but I imagine there are plenty of situations where such an approach may come in handy.

However, I still believe the old File.deleteOnExit method may be preferable as you won't need to keep the output stream open for the duration of any read operations on the file, too.

daiscog
  • 11,441
  • 6
  • 50
  • 62
  • I like your solution using `newByteChannel`. The second option, using `newBufferedReader`, works on Windows but I'm not sure if it works on Linux. I'll have to check that when I get home tonight. You see, on Linux, when you specify DELETE_ON_CLOSE the file is open and deleted so there is no directory entry on the filesystem pointing to the file, so the call to `newBufferedReader` might not find the file from the specified path, even though the file still exists. – Klitos Kyriacou Dec 18 '15 at 13:47
  • @KlitosKyriacou Yes, I was a bit concerned about that, too. Perhaps opening the reader first will help? Not really ideal, though. It seems either using deleteOnExit or just manually deleting the file when you're done (which is itself a trivial operation) is probably the safest approach. – daiscog Dec 18 '15 at 13:54
  • On second thought, I guess DELETE_ON_CLOSE might be useful when specified for *reading* the file. – Klitos Kyriacou Dec 18 '15 at 14:02