Index: loaders/src/org/openide/loaders/DataLoaderPool.java =================================================================== RCS file: /cvs/openide/loaders/src/org/openide/loaders/DataLoaderPool.java,v --- loaders/src/org/openide/loaders/DataLoaderPool.java 2 Apr 2003 09:45:50 -0000 1.2 +++ loaders/src/org/openide/loaders/DataLoaderPool.java 30 Apr 2003 17:42:44 -0000 @@ -355,9 +355,8 @@ // try to find assigned loader DataLoader pref = getPreferredLoader (fo); if (pref != null) { - DataObject obj = pref.findDataObject (fo, r); + DataObject obj = DataObjectPool.findDataObject (pref, fo, r); if (obj != null) { - DataObjectPool.getPOOL().notifyCreation (obj); // file has been recognized return obj; } @@ -367,13 +366,9 @@ java.util.Enumeration en = allLoaders (); while (en.hasMoreElements ()) { DataLoader l = (DataLoader)en.nextElement (); - DataObject obj = l.findDataObject (fo, r); + + DataObject obj = DataObjectPool.findDataObject (l, fo, r); if (obj != null) { - // the loader recognized the file - - // notify it - DataObjectPool.getPOOL().notifyCreation (obj); - return obj; } } Index: loaders/src/org/openide/loaders/DataObject.java =================================================================== RCS file: /cvs/openide/loaders/src/org/openide/loaders/DataObject.java,v --- loaders/src/org/openide/loaders/DataObject.java 2 Apr 2003 09:45:51 -0000 1.2 +++ loaders/src/org/openide/loaders/DataObject.java 30 Apr 2003 17:42:44 -0000 @@ -448,6 +448,7 @@ if (obj != null) { return obj; } + throw new DataObjectNotFoundException (fo); } catch (DataObjectExistsException ex) { return ex.getDataObject (); Index: loaders/src/org/openide/loaders/DataObjectPool.java =================================================================== RCS file: /cvs/openide/loaders/src/org/openide/loaders/DataObjectPool.java,v --- loaders/src/org/openide/loaders/DataObjectPool.java 2 Apr 2003 09:45:51 -0000 1.2 +++ loaders/src/org/openide/loaders/DataObjectPool.java 30 Apr 2003 17:42:54 -0000 @@ -32,7 +32,9 @@ * @author Jaroslav Tulach */ final class DataObjectPool extends Object -implements ChangeListener, RepositoryListener, PropertyChangeListener, Runnable { +implements ChangeListener, RepositoryListener, PropertyChangeListener { + /** set to non null if the constructor is called from somewhere else than DataObject.find */ + private static final ThreadLocal FIND = new ThreadLocal (); /** validator */ private static final Validator VALIDATOR = new Validator (); @@ -68,6 +70,30 @@ return POOL; } + /** Calls into one loader. Setups security condition to allow DataObject ocnstructor + * to succeed. + */ + public static DataObject findDataObject (DataLoader loader, FileObject fo, DataLoader.RecognizedFiles rec) + throws java.io.IOException { + DataObject ret; + + Object prev = FIND.get (); + try { + FIND.set (loader); + + ret = loader.findDataObject (fo, rec); + } finally { + FIND.set (prev); + } + + // notify it + if (ret != null) { + DataObjectPool.getPOOL().notifyCreation (ret); + } + + return ret; + } + /** Collection of all objects that has been created but their * creation has not been yet notified to OperationListener.postCreate * method. @@ -96,26 +122,17 @@ */ private ThreadLocal last = new ThreadLocal (); - /** Time when the toNotify set has been modified. - */ - private long toNotifyModified; /** A delay to check the notify modified content. It is expected that * in 500ms each constructor can finish, so 500ms after the registration * of object in a toNotify map, it should be ready and initialized. */ - private static final int SAFE_NOTIFY_DELAY = 500; + private static final int SAFE_NOTIFY_DELAY = 0; private static final Integer ONE = new Integer(1); - /** A task to check toNotify content and notify that objects were created. - */ - private RequestProcessor.Task task; - /** Constructor. */ private DataObjectPool () { - task = RequestProcessor.createRequest (this); - task.setPriority (Thread.MIN_PRIORITY); } @@ -230,11 +247,6 @@ return; } - if (toNotify.isEmpty ()) { - // ok, we do not need the task - task.cancel (); - } - // if somebody is caught in waitNotified then wake him up notifyAll (); } @@ -272,49 +284,11 @@ } - /** Invoked to periodicaly check whether some data objects are not notified - * to be created. In such case it notifies about their creation. - */ - public void run () { - Item arr []; - - synchronized (this) { - if (toNotify.isEmpty()) { - return; - } - - if (System.currentTimeMillis () < toNotifyModified + SAFE_NOTIFY_DELAY) { - task.schedule (SAFE_NOTIFY_DELAY); - return; - } - arr = (Item [])toNotify.toArray (new Item [toNotify.size ()]); - } - - // notify each created object - for (int i = 0; i < arr.length; i++) { - DataObject obj = arr[i].getDataObjectOrNull (); - - // notifyCreation removes object from toNotify queue, - // if object was already invalidated then remove it as well - if (obj != null) { - notifyCreation (obj); - } else { - synchronized (this) { - toNotify.remove (arr[i]); - } - } - } - } - /** Add to list of created objects. */ private void notifyAdd (Item item) { - if (toNotify.isEmpty()) { - task.schedule (SAFE_NOTIFY_DELAY); - } toNotify.add (item); last.set (item); - toNotifyModified = System.currentTimeMillis (); } @@ -398,6 +372,8 @@ * @exception DataObjectExistsException if the file object is already registered */ public Item register (FileObject fo, DataLoader loader) throws DataObjectExistsException { + if (FIND.get () == null) throw new IllegalStateException ("DataObject constructor can be called only thru DataObject.find - use that method"); // NOI18N + // here we're registering a listener on fo's FileSystem so we can deliver // fo changes to DO without lots of tiny listeners on folders // The new DS bound to a repository can simply place a single listener @@ -490,9 +466,6 @@ // (e.g. BB/AAA/A1) is left in the toNotify pool forever. This // point is reached; remove it now. -jglick if (toNotify.remove(item)) { - if (toNotify.isEmpty()) { - task.cancel(); - } notifyAll(); } return; Index: loaders/src/org/openide/loaders/MultiDataObject.java =================================================================== RCS file: /cvs/openide/loaders/src/org/openide/loaders/MultiDataObject.java,v --- loaders/src/org/openide/loaders/MultiDataObject.java 29 Apr 2003 09:02:27 -0000 1.3 +++ loaders/src/org/openide/loaders/MultiDataObject.java 30 Apr 2003 17:42:54 -0000 @@ -801,12 +801,8 @@ if (loader != null) { obj = loader.createMultiObject (fo); } else { - obj = (MultiDataObject)getLoader ().findDataObject (fo, RECOGNIZER); + obj = (MultiDataObject)DataObjectPool.findDataObject (getLoader (), fo, RECOGNIZER); } - - // notifies that the object has been created - DataObjectPool.getPOOL ().notifyCreation (obj); - return obj; } Index: test/unit/src/org/openide/loaders/DataObjectInvalidationTest.java =================================================================== RCS file: /cvs/openide/test/unit/src/org/openide/loaders/DataObjectInvalidationTest.java,v --- test/unit/src/org/openide/loaders/DataObjectInvalidationTest.java 30 Apr 2003 13:32:26 -0000 1.10 +++ test/unit/src/org/openide/loaders/DataObjectInvalidationTest.java 30 Apr 2003 17:42:56 -0000 @@ -83,6 +83,74 @@ TestUtilHid.destroyLocalFileSystem(name()); } */ + + public void testNobodyCanAccessDataObjectWithUnfinishedConstructor () throws Throwable { + FileSystem lfs = TestUtilHid.createLocalFileSystem(getName(), new String[] { + "folder/file.slow", + }); + final FileObject file = lfs.findResource("folder/file.slow"); + assertNotNull(file); + + final DataLoader l = DataLoader.getLoader(SlowDataLoader.class); + AddLoaderManuallyHid.addRemoveLoader(l, true); + try { + class DoTheTest extends Object implements Runnable { + private DataObject first; + /** any eception from run method */ + private Throwable ex; + + public synchronized void runInMainThread () throws Throwable { + first = DataObject.find (file); + + + // waiting for results + wait (); + if (ex != null) { + throw ex; + } + } + + public void run () { + try { + synchronized (l) { + // this wait is notified from the midle of SlowDataObject + // constructor + l.wait (700); + + // that means the thread in runInMainThread() have not finished + // the assignment to first variable yet + assertNull (first); + } + + // now try to get the DataObject while its constructor + // is blocked in the case + DataObject obj = DataObject.find (file); + assertEquals ("It is the slow obj", SlowDataObject.class, obj.getClass()); + SlowDataObject slow = (SlowDataObject)obj; + + assertNull ("The second thread still has not finished the constructor", first); + assertTrue ("Constructor has to finish completely", slow.ok); + + } catch (Throwable ex) { + this.ex = ex; + } finally { + synchronized (this) { + notify (); + } + } + } + } + + DoTheTest dtt = new DoTheTest (); + new Thread (dtt, "Slow").start (); + dtt.runInMainThread (); + + + } finally { + AddLoaderManuallyHid.addRemoveLoader(l, false); + } + TestUtilHid.destroyLocalFileSystem(getName()); + } public void testNodeDelegateNotRequestedTillObjReady() throws Exception { FileSystem lfs = TestUtilHid.createLocalFileSystem(getName(), new String[] { @@ -226,11 +294,23 @@ public static int createCount = 0; public SlowDataObject(FileObject pf, MultiFileLoader loader) throws IOException { super(pf, loader); - try { - Thread.sleep(2000); - } catch (InterruptedException ie) { - throw new IOException(ie.toString()); + synchronized (loader) { + // in case somebody is listening on the loader for our creation + // let him wake up + loader.notifyAll (); + } + + int cnt = 1; + + while (cnt-- > 0) { + try { + Thread.sleep(2000); + } catch (InterruptedException ie) { + throw new IOException(ie.toString()); + } } + + ok = true; createCount++; }