Index: loaders/src/org/openide/loaders/DataObject.java =================================================================== RCS file: /cvs/openide/loaders/src/org/openide/loaders/DataObject.java,v retrieving revision 1.3 diff -u -u -r1.3 DataObject.java --- loaders/src/org/openide/loaders/DataObject.java 16 May 2003 12:08:42 -0000 1.3 +++ loaders/src/org/openide/loaders/DataObject.java 20 May 2003 11:28:27 -0000 @@ -503,7 +503,7 @@ public final DataObject copy (final DataFolder f) throws IOException { final DataObject[] result = new DataObject[1]; FileSystem fs = f.getPrimaryFile ().getFileSystem (); - fs.runAtomicAction (new FileSystem.AtomicAction () { + invokeAtomicAction (fs, new FileSystem.AtomicAction () { public void run () throws IOException { result[0] = handleCopy (f); } @@ -529,7 +529,7 @@ synchronized ( synchObject() ) { // the object is ready to be closed FileSystem fs = getPrimaryFile ().getFileSystem (); - fs.runAtomicAction (new FileSystem.AtomicAction () { + invokeAtomicAction (fs, new FileSystem.AtomicAction () { public void run () throws IOException { handleDelete (); item.deregister(false); @@ -574,7 +574,7 @@ // executes atomic action with renaming FileSystem fs = files[0].getFileSystem (); - fs.runAtomicAction (new FileSystem.AtomicAction () { + invokeAtomicAction (fs, new FileSystem.AtomicAction () { public void run () throws IOException { files[1] = handleRename (name); if (files[0] != files[1]) @@ -610,7 +610,7 @@ // executes atomic action for moving old = getPrimaryFile (); FileSystem fs = old.getFileSystem (); - fs.runAtomicAction (new FileSystem.AtomicAction () { + invokeAtomicAction (fs, new FileSystem.AtomicAction () { public void run () throws IOException { FileObject mf = handleMove (df); item.changePrimaryFile (mf); @@ -654,7 +654,7 @@ final DataShadow[] result = new DataShadow[1]; FileSystem fs = f.getPrimaryFile ().getFileSystem (); - fs.runAtomicAction (new FileSystem.AtomicAction () { + invokeAtomicAction (fs, new FileSystem.AtomicAction () { public void run () throws IOException { result[0] = handleCreateShadow (f); } @@ -692,7 +692,7 @@ final DataObject[] result = new DataObject[1]; FileSystem fs = f.getPrimaryFile ().getFileSystem (); - fs.runAtomicAction (new FileSystem.AtomicAction () { + invokeAtomicAction (fs, new FileSystem.AtomicAction () { public void run () throws IOException { result[0] = handleCreateFromTemplate (f, name); } @@ -736,6 +736,19 @@ Object synchObject() { return nodeCreationLock; } + + /** Invokes atomic action. + */ + private void invokeAtomicAction (FileSystem fs, FileSystem.AtomicAction action) throws IOException { + if (this instanceof DataFolder) { + // action is slow + fs.runAtomicAction(action); + } else { + // it is quick, make it block DataObject recognition + DataObjectPool.getPOOL ().runAtomicAction (fs, action); + } + } + // // Property change support Index: loaders/src/org/openide/loaders/DataObjectPool.java =================================================================== RCS file: /cvs/openide/loaders/src/org/openide/loaders/DataObjectPool.java,v retrieving revision 1.3 diff -u -u -r1.3 DataObjectPool.java --- loaders/src/org/openide/loaders/DataObjectPool.java 16 May 2003 12:08:42 -0000 1.3 +++ loaders/src/org/openide/loaders/DataObjectPool.java 20 May 2003 11:28:28 -0000 @@ -142,6 +142,16 @@ return ret; } + + // + // Support for running really atomic actions + // + + public synchronized void runAtomicAction (FileSystem fs, FileSystem.AtomicAction action) + throws java.io.IOException { + fs.runAtomicAction(action); + } + /** Collection of all objects that has been created but their * creation has not been yet notified to OperationListener.postCreate * method. @@ -183,7 +193,7 @@ private DataObjectPool () { } - + /** Checks whether there is a data object with primary file * passed thru the parameter. Index: loaders/src/org/openide/loaders/DataShadow.java =================================================================== RCS file: /cvs/openide/loaders/src/org/openide/loaders/DataShadow.java,v retrieving revision 1.2 diff -u -u -r1.2 DataShadow.java --- loaders/src/org/openide/loaders/DataShadow.java 2 Apr 2003 09:45:52 -0000 1.2 +++ loaders/src/org/openide/loaders/DataShadow.java 20 May 2003 11:28:28 -0000 @@ -231,7 +231,7 @@ final FileObject fo = folder.getPrimaryFile (); final DataShadow[] arr = new DataShadow[1]; - fo.getFileSystem ().runAtomicAction (new FileSystem.AtomicAction () { + DataObjectPool.getPOOL().runAtomicAction (fo.getFileSystem (), new FileSystem.AtomicAction () { public void run () throws IOException { FileObject file = writeOriginal (name, ext, fo, original); DataObject obj = DataObject.find (file); Index: loaders/src/org/openide/loaders/DataTransferSupport.java =================================================================== RCS file: /cvs/openide/loaders/src/org/openide/loaders/DataTransferSupport.java,v retrieving revision 1.2 diff -u -u -r1.2 DataTransferSupport.java --- loaders/src/org/openide/loaders/DataTransferSupport.java 2 Apr 2003 09:45:52 -0000 1.2 +++ loaders/src/org/openide/loaders/DataTransferSupport.java 20 May 2003 11:28:32 -0000 @@ -165,7 +165,7 @@ nd.setInputText (name); if (NotifyDescriptor.OK_OPTION == DialogDisplayer.getDefault ().notify (nd)) { - trg.getPrimaryFile ().getFileSystem ().runAtomicAction (new FileSystem.AtomicAction () { + DataObjectPool.getPOOL().runAtomicAction (trg.getPrimaryFile ().getFileSystem (), new FileSystem.AtomicAction () { public void run () throws IOException { FileObject fo = trg.getPrimaryFile ().createData (nd.getInputText (), "ser"); // NOI18N FileLock lock = fo.lock (); Index: loaders/src/org/openide/loaders/InstanceDataObject.java =================================================================== RCS file: /cvs/openide/loaders/src/org/openide/loaders/InstanceDataObject.java,v retrieving revision 1.3 diff -u -u -r1.3 InstanceDataObject.java --- loaders/src/org/openide/loaders/InstanceDataObject.java 7 Apr 2003 13:18:00 -0000 1.3 +++ loaders/src/org/openide/loaders/InstanceDataObject.java 20 May 2003 11:28:33 -0000 @@ -219,7 +219,7 @@ if (newFile == null) { final FileObject[] fos = new FileObject[1]; - fo.getFileSystem().runAtomicAction(new FileSystem.AtomicAction() { + DataObjectPool.getPOOL().runAtomicAction (fo.getFileSystem(), new FileSystem.AtomicAction() { public void run () throws IOException { String fileName; if (name == null) { @@ -1375,7 +1375,7 @@ me.name = name; me.create = create; - folder.getPrimaryFile().getFileSystem().runAtomicAction(me); + DataObjectPool.getPOOL().runAtomicAction (folder.getPrimaryFile().getFileSystem(), me); me.mi = null; me.folder = null; me.instance = null; Index: test/unit/src/org/openide/loaders/SeparationOfThreadsTest.java =================================================================== RCS file: test/unit/src/org/openide/loaders/SeparationOfThreadsTest.java diff -N test/unit/src/org/openide/loaders/SeparationOfThreadsTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/loaders/SeparationOfThreadsTest.java 20 May 2003 11:28:33 -0000 @@ -0,0 +1,290 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.loaders; + +import org.openide.filesystems.*; +import org.openide.loaders.*; +import java.beans.*; +import java.io.IOException; +import junit.textui.TestRunner; +import org.netbeans.junit.*; + +/* + * Checks whether a during a modify operation (copy, move) some + * other thread can get a grip on unfinished and uncostructed + * content on filesystem. + * + * @author Jaroslav Tulach + */ +public class SeparationOfThreadsTest extends NbTestCase { + private DataFolder root; + private DataFolder to; + private DataObject a; + private DataObject b; + private DataObject res; + + /** Creates the test */ + public SeparationOfThreadsTest(String name) { + super(name); + } + + // For each test setup a FileSystem and DataObjects + protected void setUp() throws Exception { + String fsstruct [] = new String [] { + "A.attr", + "B.attr", + "dir/" + }; + TestUtilHid.destroyLocalFileSystem (getName()); + FileSystem fs = TestUtilHid.createLocalFileSystem (getName(), fsstruct); + root = DataFolder.findFolder (fs.getRoot ()); + + AddLoaderManuallyHid.addRemoveLoader (ALoader.getLoader (ALoader.class), true); + AddLoaderManuallyHid.addRemoveLoader (BLoader.getLoader (BLoader.class), true); + + to = DataFolder.findFolder (fs.findResource (fsstruct[2])); + + fs.findResource (fsstruct[0]).setAttribute ("A", Boolean.TRUE); + + a = DataObject.find (fs.findResource (fsstruct[0])); + b = DataObject.find (fs.findResource (fsstruct[1])); + + ALoader loaderA = (ALoader)ALoader.getLoader (ALoader.class); + + assertEquals ("A is loaded by ALoader", loaderA, a.getLoader()); + assertEquals ("B is loaded by BLoader", ALoader.getLoader (BLoader.class), b.getLoader()); + + + synchronized (loaderA) { + new Thread ((Runnable)loaderA).start (); + loaderA.wait (); + } + } + + //Clear all stuff when the test finish + protected void tearDown() throws Exception { + ALoader loader = (ALoader)ALoader.getLoader(ALoader.class); + synchronized (loader) { + try { + while (!loader.finished) { + loader.wait (); + } + + assertNotNull (res); + assertEquals ("The right loader synchronously", loader, res.getLoader ()); + + if (loader.asyncError != null) { + throw loader.asyncError; + } + + assertNotNull (loader.asyncRes); + assertEquals ("It is the right loader asynchronously", loader, loader.asyncRes.getLoader()); + + } finally { + loader.asyncError = null; + loader.currentThread = null; + loader.current = null; + loader.asyncRes = null; + loader.finished = false; + // clears any such flag + Thread.interrupted(); + + + TestUtilHid.destroyLocalFileSystem (getName()); + AddLoaderManuallyHid.addRemoveLoader (ALoader.getLoader (ALoader.class), false); + AddLoaderManuallyHid.addRemoveLoader (BLoader.getLoader (BLoader.class), false); + + // end of test + loader.notify (); + } + } + } + + public static void main (String[] args) throws Exception { + TestRunner.run(new NbTestSuite(SeparationOfThreadsTest.class)); + } + + + public void testCopy () throws Exception { + res = a.copy (to); + } + + public void testCreateFromTemplate () throws Exception { + res = a.createFromTemplate (to); + } + public void testMove () throws Exception { + a.move (to); + res = a; + } + public void testRename () throws Exception { + a.rename ("AnyThing"); + res = a; + } + + // + // Inner classes + // + + public static final class ALoader extends UniFileLoader implements Runnable { + DataObject asyncRes; + Exception asyncError; + FileObject current; + Thread currentThread; + boolean finished; + + public ALoader() { + super(DataObject.class.getName()); + } + protected void initialize() { + super.initialize(); + getExtensions().addExtension("attr"); + } + protected String displayName() { + return getClass().getName (); + } + protected MultiDataObject createMultiObject(FileObject pf) throws IOException { + return new MultiDataObject(pf, this); + } + + protected org.openide.loaders.MultiDataObject.Entry createPrimaryEntry(org.openide.loaders.MultiDataObject multiDataObject, org.openide.filesystems.FileObject fileObject) { + return new SlowEntry (multiDataObject, fileObject); + } + + // + // Notification that the copy is in middle + // + + public void notifyCopied (FileObject current) { + synchronized (this) { + this.current = current; + this.currentThread = Thread.currentThread (); + this.notify (); + } + try { + Thread.sleep (500); + } catch (InterruptedException ex) { + // is interrupted to be wake up sooner + } + } + + // + // The second thread that waits for the copy operation + // + public void run () { + DataLoader loader = this; + synchronized (loader) { + // continue execution in setUp + loader.notify (); + // wait for being notify about copying + try { + loader.wait (); + } catch (InterruptedException ex) { + asyncError = ex; + } + + try { + asyncRes = DataObject.find (current); + currentThread.interrupt(); + } catch (IOException ex) { + asyncError = ex; + } + + // notify that we have computed everything + finished = true; + loader.notify (); + + while (asyncRes != null && asyncError != null) { + try { + loader.wait (); + } catch (InterruptedException ex) { + } + } + } + } + + } + + public static final class BLoader extends UniFileLoader { + public BLoader() { + super(DataObject.class.getName()); + } + protected void initialize() { + super.initialize(); + getExtensions().addExtension("attr"); + } + protected String displayName() { + return getClass ().getName (); + } + protected org.openide.filesystems.FileObject findPrimaryFile(org.openide.filesystems.FileObject fileObject) { + if (Boolean.TRUE.equals (fileObject.getAttribute ("A"))) { + return null; + } + + org.openide.filesystems.FileObject retValue; + + retValue = super.findPrimaryFile(fileObject); + return retValue; + } + + protected MultiDataObject createMultiObject(FileObject pf) throws IOException { + return new MultiDataObject(pf, this); + } + } + + private static final class SlowEntry extends MultiDataObject.Entry { + public SlowEntry (MultiDataObject obj, FileObject fo) { + obj.super (fo); + } + + private void notifyCopied (FileObject fo) { + ALoader l = (ALoader)ALoader.getLoader(ALoader.class); + l.notifyCopied (fo); + } + + public org.openide.filesystems.FileObject copy(org.openide.filesystems.FileObject fileObject, String str) throws java.io.IOException { + FileObject ret = fileObject.createData ("copy", "attr"); + notifyCopied (ret); + ret.setAttribute ("A", Boolean.TRUE); + return ret; + } + + public org.openide.filesystems.FileObject createFromTemplate(org.openide.filesystems.FileObject fileObject, String str) throws java.io.IOException { + FileObject ret = fileObject.createData ("createFromTemplate", "attr"); + notifyCopied (ret); + ret.setAttribute ("A", Boolean.TRUE); + return ret; + } + + public void delete() throws java.io.IOException { + throw new IOException ("Not implemented"); + } + + public org.openide.filesystems.FileObject move(org.openide.filesystems.FileObject fileObject, String str) throws java.io.IOException { + FileObject ret = fileObject.createData ("move", "attr"); + notifyCopied (ret); + ret.setAttribute ("A", Boolean.TRUE); + super.getFile ().delete (); + return ret; + } + + public org.openide.filesystems.FileObject rename(String str) throws java.io.IOException { + FileObject ret = getFile ().getParent ().createData ("rename", "attr"); + notifyCopied (ret); + ret.setAttribute ("A", Boolean.TRUE); + super.getFile ().delete (); + return ret; + } + + } +}