+ Create a file and write its content (almost) atomically using the new + FileObject.createAndOpen + method. +
+
+ * This method usually delivers both, {@link FileChangeListener#fileDataCreated(org.openide.filesystems.FileEvent) data created}
+ * and {@link FileChangeListener#fileChanged(org.openide.filesystems.FileEvent) changed}
+ * events. Preferably it delivers them asynchronously. The assumption
+ * is that the file will be (at least partially) written before
+ * the listeners start to process the first event. The safety is additionally
+ * ensured by mutual exclusion
between output and input streams
+ * for the same file (any call to {@link #getInputStream()} will be blocked
+ * for at least two seconds if there is existing open output stream).
+ * If you finish writing the content of your file in those two seconds,
+ * you can be sure, nobody will have read its content yet.
+ *
this
is not a folder
+ * @since 7.40
+ */
+ public OutputStream createAndOpen(final String name) throws IOException {
+ class R implements FileSystem.AsyncAtomicAction {
+ OutputStream os;
+
+ @Override
+ public void run() throws IOException {
+ FileObject fo = createData(name);
+ os = fo.getOutputStream();
+ }
+
+ @Override
+ public boolean isAsynchronous() {
+ return true;
+ }
+ }
+ R r = new R();
+ getFileSystem().runAtomicAction(r);
+ return r.os;
+ }
/** Test whether this file can be written to or not.
* --- a/openide.filesystems/src/org/openide/filesystems/FileSystem.java Fri Sep 03 15:30:16 2010 +0200 +++ a/openide.filesystems/src/org/openide/filesystems/FileSystem.java Fri Sep 03 16:23:47 2010 +0200 @@ -841,6 +841,10 @@ */ public void run() throws IOException; } + + static interface AsyncAtomicAction extends AtomicAction { + boolean isAsynchronous(); + } /** Allows a filesystem to annotate a group of files (typically comprising a data object) with additional markers. *
This could be useful, for --- a/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java Fri Sep 03 15:30:16 2010 +0200 +++ a/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java Fri Sep 03 16:23:47 2010 +0200 @@ -324,6 +324,76 @@ value.equals((String)fo3.getAttribute(attrName)) ); fileDataCreatedAssert("parent should fire fileDataCreated",1); } + + public void testCreateAndOpen() throws Exception { + checkSetUp(); + FileObject f; + try { + f = getTestFolder1(root).createFolder("createAndOpen"); + } catch (IOException iex) { + fsAssert("If read only FS, then OK to fail", + fs.isReadOnly()); + return; + } + final FileObject fold = f; + + class L extends FileChangeAdapter { + String dataCreated; + String changed; + + @Override + public synchronized void fileDataCreated(FileEvent fe) { + try { + FileObject ch = fold.getFileObject("child.txt"); + assertNotNull("Child found", ch); + + dataCreated = ch.asText(); + notifyAll(); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + @Override + public synchronized void fileChanged(FileEvent fe) { + try { + FileObject ch = fold.getFileObject("child.txt"); + assertNotNull("Child found", ch); + + changed = ch.asText(); + notifyAll(); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + + public synchronized void waitEvents() throws InterruptedException { + for (int i = 0; i < 100; i++) { + if (changed != null && dataCreated != null) { + break; + } + wait(100); + } + } + } + + L l = new L(); + fold.addFileChangeListener(l); + assertEquals("No children", 0, fold.getChildren().length); + + OutputStream os = fold.createAndOpen("child.txt"); + os.write("Ahoj".getBytes()); + os.close(); + + FileObject ch = fold.getFileObject("child.txt"); + assertNotNull("Child found", ch); + assertEquals("Right content now", "Ahoj", ch.asText()); + + l.waitEvents(); + assertEquals("Right content when changed", "Ahoj", l.changed); + assertEquals("Right content when created", "Ahoj", l.dataCreated); + } /** Test of copy method, of class org.openide.filesystems.FileObject. */ public void testCopy1_FS() throws IOException {