1

For more detailed information regarding the motivation behind this goal (and my efforts to solve it) view my previous question. I decided to ask this as a new question entirely as I thought that it had evolved sufficiently to merit doing so. As a summary, I intend to use JDOM in combination with NIO in order to:

  1. Gain an exclusive file lock on an xml file.
  2. Read the file into a Document object.
  3. Make arbitrary changes (with lock still active!).
  4. Write the changes back to the xml file.
  5. Release the file lock.

The issue which I am getting however is that the built-in code to read the xml file into a document object closes the channel (and therefore releases the lock), as seen below:

import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import javax.xml.parsers.*;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class Test4{ 
    String path = "Test 2.xml";
    private DocumentBuilderFactory dbFactory;
    private DocumentBuilder dBuilder;
    private Document doc;

    public Test4(){
        try (final FileChannel channel = new RandomAccessFile(new File(path), "rw").getChannel()) {
            dbFactory = DocumentBuilderFactory.newInstance();
            dBuilder = dbFactory.newDocumentBuilder();

            System.out.println(channel.isOpen());
            doc = dBuilder.parse(Channels.newInputStream(channel));
            System.out.println(channel.isOpen());

            channel.close();
        } catch (IOException | ParserConfigurationException | SAXException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        new Test4();
    }
}

Output:

true
false

Having looked through the documentation and trawled the built in Java libraries, I am really struggling to even find where the channel is closed, let alone how to prevent it closing. Any pointers would be great! Thanks.

Community
  • 1
  • 1
Hungry
  • 1,645
  • 1
  • 16
  • 26
  • `DocumentBuilder.parse()` is closing the supplied input stream. – user207421 Oct 28 '14 at 11:06
  • Sorry the question is a little misleading, I meant that I could not figure out where the built in DocumentBuilder class closed the channel in order to override the behaviour. Overriding the default close behaviour of `FilterInputStream`(as the accepted answer does) seems to achieve the same result though. – Hungry Oct 28 '14 at 11:14

2 Answers2

4

One clean way to do this is to create a FilterInputStream and override the close to do nothing:

public Test() {
    try {
        channel = new RandomAccessFile(new File(path), "rw").getChannel();
        dbFactory = DocumentBuilderFactory.newInstance();
        dBuilder = dbFactory.newDocumentBuilder();

        System.out.println(channel.isOpen());
        NonClosingInputStream ncis = new NonClosingInputStream(Channels.newInputStream(channel));
        doc = dBuilder.parse(ncis);
        System.out.println(channel.isOpen());
        // Closes here.
        ncis.reallyClose();
        channel.close(); //Redundant
    } catch (IOException | ParserConfigurationException | SAXException e) {
        e.printStackTrace();
    }
}

class NonClosingInputStream extends FilterInputStream {

    public NonClosingInputStream(InputStream it) {
        super(it);
    }

    @Override
    public void close() throws IOException {
        // Do nothing.
    }

    public void reallyClose() throws IOException {
        // Actually close.
        in.close();
    }
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
-1

Did you try the try-with-ressource statment? Even though, this is not the main purpose of this feature, maybe that does what you are looking for (in form of auto closing the ressources (your channel), but only when you leave the corresponding try-block)

try (final Channel channel = new RandomAccessFile(new File(path), "rw").getChannel()) {
  // your stuff here
} catch (IOException ex) {
  ex.printStackTrace();
}
ifloop
  • 8,079
  • 2
  • 26
  • 35
  • Hi and thanks for the response. Unfortunately I just tried this, however it had seemingly little effect. It seems a little cleaner though so I will probably keep it anyway! – Hungry Oct 28 '14 at 10:26
  • This closes the channel. That's exactly what he is trying to *prevent.* Do read the question. – user207421 Oct 28 '14 at 11:03
  • @EJP I am aware of the closing (do read my answer), I just contributed this as an idea for keeping the lock after the call to `dBuilder.parse()`, that was OP's need. Do read the question. – ifloop Oct 28 '14 at 11:16