Search code examples
javawindowsfile-locking

Java: issue with channel File lock in Windows


I'm writing a little program in Java writing data into an XML file. As there are more than one user launching it, I need that the xml file, before beeing written, is locked. So I've written like that:

if (new File(path).exists()) {

        try {

            FileChannel channel = new RandomAccessFile(new File(path), "rw")
                    .getChannel();

            java.nio.channels.FileLock lock = channel.lock();

            DocumentBuilderFactory docFactory = DocumentBuilderFactory
                    .newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            Document doc = docBuilder.parse(path);

            // Get the root element
            Node economato = doc.getFirstChild();

            NodeList protocolli = doc.getElementsByTagName("numero");

            if (protocolli.getLength() == 0)
                p.setNumero(1);

            else
                p.setNumero(protocolli.getLength()+1);

            Element protocolloNode = doc.createElement("protocollo");
            economato.appendChild(protocolloNode);

            Element protocolloNumero = doc.createElement("numero");
            protocolloNumero.appendChild(doc.createTextNode(Integer
                    .toString(p.getNumero())));
            protocolloNode.appendChild(protocolloNumero);

            Element protocolloData = doc.createElement("data");
            protocolloData.appendChild(doc.createTextNode(p.getData()));
            protocolloNode.appendChild(protocolloData);

            Element protocolloObj = doc.createElement("oggetto");
            protocolloObj.appendChild(doc.createTextNode(p.getOggetto()));
            protocolloNode.appendChild(protocolloObj);

            Element protocolloDest = doc.createElement("destinatario");
            protocolloDest.appendChild(doc.createTextNode(p
                    .getDestinatario()));
            protocolloNode.appendChild(protocolloDest);

            Element protocolloOp = doc.createElement("operatore");
            protocolloOp.appendChild(doc.createTextNode(p.getOperatore()));
            protocolloNode.appendChild(protocolloOp);

            TransformerFactory transformerFactory = TransformerFactory
                    .newInstance();
            Transformer transformer = transformerFactory.newTransformer();

            DOMSource source = new DOMSource(doc);
            StreamResult result = new StreamResult(path);

            transformer.transform(source, result);

            lock.release();
            channel.close();

            JOptionPane.showMessageDialog(this.fatherFrame,
                    "Nuovo protocollo salvato correttamente!");

        } catch (ParserConfigurationException pce) {
            pce.printStackTrace();
        } catch (TransformerException tfe) {
            tfe.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (SAXException sae) {
            sae.printStackTrace();
        }
    }

I've tried to launch in in Linux Debian and everything works fine. Instead, if I try in Windows 7 I get this error:

java.io.IOException: The process cannot access the file because another process has locked a portion of the file
at java.io.FileInputStream.readBytes(Native Method)
a java.io.FileInputStream.read(Unknown Source)
at java.io.BufferedInputStream.fill(Unknown Source)
at java.io.BtufferedInputStream.read(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager$RewindableInputStream.read(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Unknown Source)
at javax.xml.parsers.DocumentBuilder.parse(Unknown Source)
at it.questura.economato.InsertListener.actionPerformed(InsertListener.java:77)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

But there's no other process blocking the file. I've launched only an instance of my program so it's unique...which process could be locking the file?

---EDIT--- The only solution I have is to use a "token file" called LOCK. You get the lock on the channel on the file called LOCK and then you can read/write on the real file.xml...then you release the lock on the LOCK file...it seems to work but I find it silly and stupid. Isn't there anything better? I've also read, in a forum, that I could read() the file channel into a ByteBuffer and then create a ByteArrayInputStream from the byte [] backing it. Then you pass the stream to DocumentBuilder. But when I create a ByteBuffer, before I should allocate its number of bytes...how can I know in advance how many bytes is the file.xml long?


Solution

  • Do not use parse(String path) . Use the method that takes a Reader or InputStream and wrap your FileChannel in it.

    Like this: Channels.newInputStream(channel)