KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > loaders > DataObjectPool


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.loaders;
21
22
23 import java.lang.ref.*;
24 import java.util.*;
25 import java.util.logging.*;
26 import javax.swing.event.ChangeListener JavaDoc;
27 import org.openide.filesystems.*;
28 import org.openide.util.*;
29
30 /** Registration of all data objects in the system.
31 * Maps data objects to its handlers.
32 *
33 * @author Jaroslav Tulach
34 */

35 final class DataObjectPool extends Object JavaDoc
36 implements ChangeListener JavaDoc {
37     /** set to null if the constructor is called from somewhere else than DataObject.find
38      * Otherwise contains items that have just been created in this thread and
39      * shall be notified.
40      */

41     private static final ThreadLocal JavaDoc<Collection<Item>> FIND = new ThreadLocal JavaDoc<Collection<Item>>();
42     /** validator */
43     private static final Validator VALIDATOR = new Validator ();
44
45     private static final Collection<Item> TOKEN = Collections.unmodifiableList(new ArrayList<Item>());
46
47     /** assignes file objects a unique instance of Item, if it has been created
48      */

49     private Map<FileObject,Item> map = new HashMap<FileObject,Item>(512) {
50         public Item put(FileObject obj, Item item) {
51             Item prev = super.put(obj, item);
52             FileObject parent = obj.getParent();
53             if (parent == null) {
54                 return prev;
55             }
56             List<Item> arr = children.get(parent);
57             if (arr == null) {
58                 arr = new ArrayList<Item>(parent.getChildren().length);
59             }
60             arr.add(item);
61             return prev;
62         }
63         public Item remove(Object JavaDoc obj) {
64             Item prev = super.remove(obj);
65             if (! (obj instanceof FileObject)) {
66                 return prev;
67             }
68             
69             FileObject parent = ((FileObject)obj).getParent();
70             if (parent == null) {
71                 return prev;
72             }
73             List<Item> arr = children.get(parent);
74             if (arr != null) {
75                 arr.remove(obj);
76                 if (arr.isEmpty()) {
77                     children.remove(parent);
78                 }
79             }
80             return prev;
81         }
82     };
83     /** map that assigns to each folder list of Items created for its children */
84     private Map<FileObject,List<Item>> children = new HashMap<FileObject, List<Item>>();
85     
86     /** covers all FileSystems we're listening on */
87     private Set<FileSystem> knownFileSystems = new WeakSet<FileSystem>();
88     
89     /** error manager to log what is happening here */
90     private static final Logger err = Logger.getLogger("org.openide.loaders.DataObject.find"); // NOI18N
91

92     /** the pool for all objects. Use getPOOL method instead of direct referencing
93      * this field.
94      */

95     private static DataObjectPool POOL;
96
97     /** Lock for creating POOL instance */
98     private static Object JavaDoc lockPOOL = new Object JavaDoc();
99
100     /** check to know if someone is waiting in waitNotified, changed from
101      * inside synchronized block, but read without synchronization, that is
102      * why it is made volatile
103      */

104     private volatile long inWaitNotified = -1;
105     
106     /** Get the instance of DataObjectPool - value of static field 'POOL'.
107      * Initialize the field if necessary.
108      *
109      * @return The DataObjectPool.
110      */

111     static DataObjectPool getPOOL() {
112         synchronized (lockPOOL) {
113             if (POOL != null)
114                 return POOL;
115             POOL = new DataObjectPool ();
116         }
117         
118         lp.addChangeListener(POOL);
119
120         return POOL;
121     }
122     
123     /** Allows DataObject constructors to be called.
124      * @return a key to pass to exitAllowConstructor
125      */

126     private static Collection<Item> enterAllowContructor() {
127         Collection<Item> prev = FIND.get();
128         FIND.set (TOKEN);
129         return prev;
130     }
131     
132     /** Disallows DataObject constructors to be called and notifies
133      * all created DataObjects.
134      */

135     private static void exitAllowConstructor(Collection<Item> previous) {
136         Collection<Item> l = FIND.get ();
137         FIND.set (previous);
138         if (l != TOKEN) getPOOL ().notifyCreationAll(l);
139     }
140     
141     /** Method to check whether the constructor is allowed.
142      */

143     final static boolean isConstructorAllowed() {
144         return FIND.get() != null;
145     }
146
147     /** Calls into one loader. Setups security condition to allow DataObject ocnstructor
148      * to succeed.
149      */

150     public static DataObject handleFindDataObject (DataLoader loader, FileObject fo, DataLoader.RecognizedFiles rec)
151     throws java.io.IOException JavaDoc {
152         DataObject ret;
153         
154         Collection<Item> prev = enterAllowContructor();
155         try {
156             // make sure this thread is allowed to recognize
157
getPOOL ().enterRecognition(fo);
158             
159             ret = loader.handleFindDataObject (fo, rec);
160         } finally {
161             exitAllowConstructor (prev);
162         }
163         
164         return ret;
165     }
166
167     /** Creates and finishes registration of MultiDataObject.
168      */

169     public static MultiDataObject createMultiObject (MultiFileLoader loader, FileObject fo)
170     throws java.io.IOException JavaDoc {
171         MultiDataObject ret;
172         
173         Collection<Item> prev = enterAllowContructor();
174         try {
175             ret = loader.createMultiObject (fo);
176         } finally {
177             exitAllowConstructor (prev);
178         }
179         
180         return ret;
181      }
182     
183     /** Calls into FolderLoader. Setups security condition to allow DataObject constructor
184      * to succeed.
185      */

186     public static MultiDataObject createMultiObject(DataLoaderPool.FolderLoader loader, FileObject fo, DataFolder original) throws java.io.IOException JavaDoc {
187         MultiDataObject ret;
188         
189         Collection<Item> prev = enterAllowContructor();
190         try {
191             ret = loader.createMultiObject (fo, original);
192         } finally {
193             exitAllowConstructor (prev);
194         }
195         
196         return ret;
197      }
198     
199         
200     
201     /** Executes atomic action with privilege to create DataObjects.
202      */

203     public void runAtomicActionSimple (FileObject fo, FileSystem.AtomicAction action)
204     throws java.io.IOException JavaDoc {
205         Collection<Item> prev = enterAllowContructor();
206         try {
207             fo.getFileSystem ().runAtomicAction(action);
208         } finally {
209             exitAllowConstructor (prev);
210         }
211     }
212     
213     //
214
// Support for running really atomic actions
215
//
216
private Thread JavaDoc atomic;
217     private RequestProcessor privileged;
218     /** the folder that is being modified */
219     private FileObject blocked;
220     public void runAtomicAction (final FileObject target, final FileSystem.AtomicAction action)
221     throws java.io.IOException JavaDoc {
222         
223         class WrapAtomicAction implements FileSystem.AtomicAction {
224             public void run () throws java.io.IOException JavaDoc {
225                 Thread JavaDoc prev;
226                 FileObject prevBlocked;
227                 synchronized (DataObjectPool.this) {
228                     // make sure that we are the ones that own
229
// the recognition process
230
enterRecognition (null);
231                     prev = atomic;
232                     prevBlocked = blocked;
233                     atomic = Thread.currentThread ();
234                     blocked = target;
235                 }
236
237                 Collection<Item> findPrev = enterAllowContructor();
238                 try {
239                     action.run ();
240                 } finally {
241                     synchronized (DataObjectPool.this) {
242                         atomic = prev;
243                         blocked = prevBlocked;
244                         DataObjectPool.this.notifyAll ();
245                     }
246                     exitAllowConstructor (findPrev);
247                 }
248             }
249         } // end of WrapAtomicAction
250

251         target.getFileSystem ().runAtomicAction(new WrapAtomicAction ());
252     }
253     
254     /** The thread that runs in atomic action wants to delegate its privilege
255      * to somebody else. Used in DataFolder.getChildren that blocks on
256      * Folder Recognizer thread.
257      *
258      * @param delegate the privileged processor
259      */

260     public synchronized void enterPrivilegedProcessor(RequestProcessor delegate) {
261         if (atomic == Thread.currentThread()) {
262             if (privileged != null) throw new IllegalStateException JavaDoc ("Previous privileged is not null: " + privileged + " now: " + delegate); // NOI18N
263
privileged = delegate;
264         }
265         // wakeup everyone in enterRecognition, as this changes the conditions there
266
notifyAll ();
267     }
268     
269     /** Exits the privileged processor.
270      */

271     public synchronized void exitPrivilegedProcessor(RequestProcessor delegate) {
272         if (atomic == Thread.currentThread ()) {
273             if (privileged != delegate) throw new IllegalStateException JavaDoc ("Trying to unregister wrong privileged. Prev: " + privileged + " now: " + delegate); // NOI18N
274
privileged = null;
275         }
276         // wakeup everyone in enterRecognition, as this changes the conditions there
277
notifyAll ();
278     }
279     
280     /** Ensures it is safe to enter the recognition.
281      * @param fo file object we want to recognize or null if we do not know it
282      */

283     private synchronized void enterRecognition (FileObject fo) {
284         // wait till nobody else stops the recognition
285
for (;;) {
286             if (atomic == null) {
287                 // ok, I am the one who can enter
288
break;
289             }
290             if (atomic == Thread.currentThread()) {
291                 // ok, reentering again
292
break;
293             }
294             
295             if (privileged != null && privileged.isRequestProcessorThread()) {
296                 // ok, we have privileged request processor thread
297
break;
298             }
299             
300             if (fo != null && blocked != null && !blocked.equals (fo.getParent ())) {
301                 // access to a file in different folder than it is blocked
302
// => go on
303
break;
304             }
305             
306             if (err.isLoggable(Level.FINE)) {
307                 err.fine("Enter recognition block: " + Thread.currentThread()); // NOI18N
308
err.fine(" waiting for: " + fo); // NOI18N
309
err.fine(" blocking thread: " + atomic); // NOI18N
310
err.fine(" blocked on: " + blocked); // NOI18N
311
}
312             try {
313                 if (FolderList.isFolderRecognizerThread()) {
314                     inWaitNotified = System.currentTimeMillis();
315                 }
316                 wait ();
317             } catch (InterruptedException JavaDoc ex) {
318                 // means nothing, go on
319
} finally {
320                 if (FolderList.isFolderRecognizerThread()) {
321                     inWaitNotified = -1;
322                 }
323             }
324         }
325     }
326     
327     /** Collection of all objects that has been created but their
328     * creation has not been yet notified to OperationListener.postCreate
329     * method.
330     */

331     private Set<Item> toNotify = new HashSet<Item>();
332     
333     /** Constructor.
334      */

335     private DataObjectPool () {
336     }
337
338     /** Checks whether there is a data object with primary file
339     * passed thru the parameter.
340     *
341     * @param fo the file to check
342     * @return data object with fo as primary file or null
343     */

344     public DataObject find (FileObject fo) {
345         synchronized (this) {
346             Item doh = map.get(fo);
347             if (doh == null) {
348                 return null;
349             }
350             
351             // do not return DOs before their creation were notified to OperationListeners
352
if (toNotify.contains (doh)) {
353                 // special test for data objects calling this method from
354
// their own constructor, those are ok to be returned if
355
// they exist
356
Collection<Item> l = FIND.get();
357                 if (l == null || !l.contains (doh)) {
358                     return null;
359                 }
360             }
361
362             return doh.getDataObjectOrNull ();
363         }
364     }
365     
366     /** mapping of files to registration count */
367     private final Map<FileObject,Integer JavaDoc> registrationCounts = new WeakHashMap<FileObject,Integer JavaDoc>();
368     void countRegistration(FileObject fo) {
369         Integer JavaDoc i = registrationCounts.get(fo);
370         Integer JavaDoc i2;
371         if (i == null) {
372             i2 = 1;
373         } else {
374             i2 = i + 1;
375         }
376         registrationCounts.put(fo, i2);
377     }
378     /** For use from FolderChildren. @see "#20699" */
379     int registrationCount(FileObject fo) {
380         Integer JavaDoc i = registrationCounts.get(fo);
381         if (i == null) {
382             return 0;
383         } else {
384             return i;
385         }
386     }
387     
388     /** Refresh of all folders.
389     */

390     private void refreshAllFolders () {
391         Set<FileObject> files;
392         synchronized (this) {
393             files = new HashSet<FileObject>(map.keySet());
394         }
395
396         for (FileObject fo : files) {
397             if (fo.isFolder ()) {
398                 DataObject obj = find (fo);
399                 if (obj instanceof DataFolder) {
400                     DataFolder df = (DataFolder)obj;
401                     FileObject file = df.getPrimaryFile ();
402                     synchronized (this) {
403                         if (toNotify.isEmpty() || !toNotify.contains(map.get(file))) {
404                             FolderList.changedDataSystem (file);
405                         }
406                     }
407                 }
408             }
409         }
410     }
411
412     /** Rescans all fileobjects in given set.
413     * @param s mutable set of FileObjects
414     * @return set of DataObjects that refused to be revalidated
415     */

416     public Set<DataObject> revalidate (Set<FileObject> s) {
417         return VALIDATOR.revalidate (s);
418     }
419
420     /** Rescan all primary files of currently existing data
421     * objects.
422     *
423     * @return set of DataObjects that refused to be revalidated
424     */

425     public Set<DataObject> revalidate () {
426         Set<FileObject> files;
427         synchronized (this) {
428             files = createSetOfAllFiles (map.values ());
429         }
430
431         return revalidate (files);
432     }
433
434     /** Notifies that an object has been created.
435      * @param obj the object that was created
436     */

437     public void notifyCreation (DataObject obj) {
438         notifyCreation (obj.item);
439     }
440
441     private static final DataLoaderPool lp = DataLoaderPool.getDefault();
442     
443     /** Notifies the creation of an item*/
444     private void notifyCreation (Item item) {
445         synchronized (this) {
446             if (err.isLoggable(Level.FINE)) {
447                 err.fine("Notify created: " + item + " by " + Thread.currentThread()); // NOI18N
448
}
449             
450             if (toNotify.isEmpty()) {
451                 if (err.isLoggable(Level.FINE)) {
452                     err.fine(" but toNotify is empty"); // NOI18N
453
}
454                 return;
455             }
456             
457             if (!toNotify.remove (item)) {
458                 if (err.isLoggable(Level.FINE)) {
459                     err.fine(" the item is not there: " + toNotify); // NOI18N
460
}
461                 return;
462             }
463             
464             // if somebody is caught in waitNotified then wake him up
465
notifyAll ();
466         }
467         
468         DataObject obj = item.getDataObjectOrNull ();
469         if (obj != null) {
470             lp.fireOperationEvent (
471                 new OperationEvent (obj), OperationEvent.CREATE
472             );
473         }
474     }
475     
476     /** Notifies all objects in the list */
477     private void notifyCreationAll(Collection<Item> l) {
478         if (l.isEmpty()) return;
479         for (Item i : l) {
480             notifyCreation (i);
481         }
482     }
483     
484     /** Wait till the data object will be notified. But wait limited amount
485      * of time so we will not deadlock
486      *
487      * @param obj data object to check
488      */

489     public void waitNotified (DataObject obj) {
490         for (;;) {
491             synchronized (this) {
492                 try {
493                     enterRecognition (obj.getPrimaryFile().getParent());
494
495                     if (toNotify.isEmpty()) {
496                         return;
497                     }
498
499                     Collection<Item> l = FIND.get ();
500                     if (l != null && l.contains (obj.item)) {
501                         return;
502                     }
503
504                     if (!toNotify.contains (obj.item)) {
505                         return;
506                     }
507
508                     if (err.isLoggable(Level.FINE)) {
509                         err.fine("waitTillNotified: " + Thread.currentThread()); // NOI18N
510
err.fine(" waitingFor: " + obj.getPrimaryFile ().getPath ()); // NOI18N
511
}
512
513                     if (FolderList.isFolderRecognizerThread()) {
514                         inWaitNotified = System.currentTimeMillis();
515                     }
516                     wait ();
517                 } catch (InterruptedException JavaDoc ex) {
518                     // never mind
519
} finally {
520                     if (FolderList.isFolderRecognizerThread()) {
521                         inWaitNotified = -1;
522                     }
523                 }
524             }
525         }
526     }
527
528     /** Allows to check whether folder recognizer is in waitNotified method in order
529      * to detect more precisly the condition needed for deadlock #65543 really
530      * happened.
531      * @return the time for how long the folder recognizer is waiting or -1 if it is not
532      */

533     final long timeInWaitNotified() {
534         long l = inWaitNotified;
535         if (l == -1) {
536             return -1;
537         } else {
538             l = System.currentTimeMillis() - l;
539             if (l < 0) {
540                 l = 0;
541             }
542             return l;
543         }
544     }
545     
546     
547     /** Add to list of created objects.
548      */

549     private void notifyAdd (Item item) {
550         toNotify.add (item);
551         Collection<Item> l = FIND.get ();
552         if (l == TOKEN) FIND.set(l = new ArrayList<Item>());
553         l.add (item);
554     }
555     
556     private static final Logger LISTENER = Logger.getLogger("org.openide.loaders.DataObjectPool.Listener"); // NOI18N
557

558     
559     /** Listener used to distribute the File events to their DOs.
560      * [pnejedly] A little bit about its internals/motivation:
561      * Originally, every created DO have hooked its onw listener to the primary
562      * FO's parent folder for listening on primary FO changes. The listener
563      * was enhanced in MDO to also cover secondaries.
564      * Now there is one FSListener per FileSystem which have to distribute
565      * the events to the DOs using limited DOPool's knowledge about FO->DO
566      * mapping. Because the mapping knowledge is limited to primary FOs only,
567      * it have to resort to notifying all known DOs for given folder
568      * if the changed file is not known. Although it is not as good as direct
569      * notification used for known primaries, it is still no worse than
570      * all DOs listening on their folder themselves as it spares at least
571      * the zillions of WeakListener instances.
572      */

573     private final class FSListener extends FileChangeAdapter {
574         FSListener() {}
575         private Collection<Item> getTargets(FileEvent fe) {
576             FileObject fo = fe.getFile();
577             List<Item> toNotify = new LinkedList<Item>();
578             // The FileSystem notifying us about the changes should
579
// not hold any lock so we're safe here
580
synchronized (DataObjectPool.this) {
581                 Item itm = map.get(fo);
582                 if (itm != null) { // the file was someones' primary
583
return Collections.singleton(itm); // so notify only owner
584
} else { // unknown file or someone secondary
585
List<Item> arr = children.get(fo.getParent());
586                     if (arr != null) {
587                         return new ArrayList<Item>(arr);
588                     } else {
589                         return Collections.emptyList();
590                     }
591                 }
592             }
593         }
594
595         public void fileChanged(FileEvent fe) {
596             if (LISTENER.isLoggable(Level.FINE)) {
597                 LISTENER.fine("fileChanged: " + fe); // NOI18N
598
}
599             for (Item item : getTargets(fe)) {
600                 DataObject dobj = item.getDataObjectOrNull();
601                 if (LISTENER.isLoggable(Level.FINE)) {
602                     LISTENER.fine(" to: " + dobj); // NOI18N
603
}
604                 if (dobj != null) dobj.notifyFileChanged(fe);
605             }
606         }
607
608         public void fileRenamed (FileRenameEvent fe) {
609             if (LISTENER.isLoggable(Level.FINE)) {
610                 LISTENER.fine("fileRenamed: " + fe); // NOI18N
611
}
612             for (Item item : getTargets(fe)) {
613                 DataObject dobj = item.getDataObjectOrNull();
614                 if (LISTENER.isLoggable(Level.FINE)) {
615                     LISTENER.fine(" to: " + dobj); // NOI18N
616
}
617                 if (dobj != null) dobj.notifyFileRenamed(fe);
618             }
619         }
620
621         public void fileDeleted (FileEvent fe) {
622             if (LISTENER.isLoggable(Level.FINE)) {
623                 LISTENER.fine("fileDeleted: " + fe); // NOI18N
624
}
625             for (Item item : getTargets(fe)) {
626                 DataObject dobj = item.getDataObjectOrNull();
627                 if (LISTENER.isLoggable(Level.FINE)) {
628                     LISTENER.fine(" to: " + dobj); // NOI18N
629
}
630                 if (dobj != null) dobj.notifyFileDeleted(fe);
631             }
632         }
633
634         public void fileDataCreated (FileEvent fe) {
635             if (LISTENER.isLoggable(Level.FINE)) {
636                 LISTENER.fine("fileDataCreated: " + fe); // NOI18N
637
}
638             for (Item item : getTargets(fe)) {
639                 DataObject dobj = item.getDataObjectOrNull();
640                 if (LISTENER.isLoggable(Level.FINE)) {
641                     LISTENER.fine(" to: " + dobj); // NOI18N
642
}
643                 if (dobj != null) dobj.notifyFileDataCreated(fe);
644             }
645             ShadowChangeAdapter.checkBrokenDataShadows(fe);
646         }
647         
648         public void fileAttributeChanged (FileAttributeEvent fe) {
649             if (LISTENER.isLoggable(Level.FINE)) {
650                 LISTENER.fine("fileAttributeChanged: " + fe); // NOI18N
651
}
652             for (Item item : getTargets(fe)) {
653                 DataObject dobj = item.getDataObjectOrNull();
654                 if (LISTENER.isLoggable(Level.FINE)) {
655                     LISTENER.fine(" to: " + dobj); // NOI18N
656
}
657                 if (dobj != null) dobj.notifyAttributeChanged(fe);
658             }
659         }
660
661         public void fileFolderCreated(FileEvent fe) {
662             if (LISTENER.isLoggable(Level.FINE)) {
663                 LISTENER.fine("fileFolderCreated: " + fe); // NOI18N
664
}
665             ShadowChangeAdapter.checkBrokenDataShadows(fe);
666         }
667     }
668     
669     /** Registers new DataObject instance.
670     * @param fo primary file for obj
671     * @param loader the loader of the object to be created
672     *
673     * @return object with common information for this <CODE>DataObject</CODE>
674     * @exception DataObjectExistsException if the file object is already registered
675     */

676     public Item register (FileObject fo, DataLoader loader) throws DataObjectExistsException {
677         if (FIND.get () == null) throw new IllegalStateException JavaDoc ("DataObject constructor can be called only thru DataObject.find - use that method"); // NOI18N
678

679         // here we're registering a listener on fo's FileSystem so we can deliver
680
// fo changes to DO without lots of tiny listeners on folders
681
// The new DS bound to a repository can simply place a single listener
682
// on its repository instead of registering listeners on FileSystems.
683
try { // to register a listener of fo's FileSystem
684
FileSystem fs = fo.getFileSystem();
685             synchronized (knownFileSystems) {
686                 if (! knownFileSystems.contains(fs)) {
687                     fs.addFileChangeListener (new FSListener());
688                     knownFileSystems.add(fs);
689                 }
690             }
691         } catch (FileStateInvalidException e ) {
692             // no need to listen then
693
}
694         
695         Item doh;
696         DataObject obj;
697         synchronized (this) {
698             doh = map.get(fo);
699             // if Item for this file has not been created yet
700
if (doh == null) {
701                 doh = new Item (fo);
702                 map.put (fo, doh);
703                 countRegistration(fo);
704                 notifyAdd (doh);
705
706                 VALIDATOR.notifyRegistered (fo);
707
708                 return doh;
709             }
710             
711             obj = doh.getDataObjectOrNull ();
712
713             if (obj == null) {
714                 // the item is to be finalize => create new
715
doh = new Item (fo);
716                 map.put (fo, doh);
717                 countRegistration(fo);
718                 notifyAdd (doh);
719
720                 return doh;
721             }
722             
723             if (!VALIDATOR.reregister (obj, loader)) {
724                 throw new DataObjectExistsException (obj);
725             }
726         }
727         
728         try {
729             obj.setValid (false);
730             synchronized (this) {
731                 // check if there isn't any new data object registered
732
// when this thread left synchronization block.
733
Item doh2 = map.get(fo);
734                 if (doh2 == null) {
735                     doh = new Item (fo);
736                     map.put (fo, doh);
737                     countRegistration(fo);
738                     notifyAdd (doh);
739
740                     return doh;
741                 }
742             }
743         } catch (java.beans.PropertyVetoException JavaDoc ex) {
744             VALIDATOR.refusingObjects.add (obj);
745         }
746         throw new DataObjectExistsException (obj);
747     }
748
749     /** Notifies all newly created objects to
750
751     /** Deregister.
752     * @param item the item with common information to deregister
753     * @param refresh true if the parent folder should be refreshed
754     */

755     private synchronized void deregister (Item item, boolean refresh) {
756         FileObject fo = item.primaryFile;
757
758         Item previous = map.remove(fo);
759
760         if (previous != null && previous != item) {
761             // ops, mistake,
762
// return back the original
763
map.put (fo, previous);
764             countRegistration(fo);
765             // Furthermore, item is probably in toNotify by mistake.
766
// Observed in DataFolderTest.testMove: after vetoing the move
767
// of a data folder, the bogus item for the temporary new folder
768
// (e.g. BB/AAA/A1) is left in the toNotify pool forever. This
769
// point is reached; remove it now. -jglick
770
if (toNotify.remove(item)) {
771                 notifyAll();
772             }
773             return;
774         }
775
776         // refresh of parent folder
777
if (refresh) {
778             fo = fo.getParent ();
779             if (fo != null) {
780                 Item item2 = map.get (fo);
781                 if (item2 != null) {
782                     DataFolder df = (DataFolder) item2.getDataObjectOrNull();
783                     if (df != null) {
784                         VALIDATOR.refreshFolderOf (df);
785                     }
786                 }
787             }
788         }
789     }
790
791     /** Changes the primary file to new one.
792     * @param item the item to change
793     * @param newFile new primary file to set
794     */

795     private synchronized void changePrimaryFile (
796         Item item, FileObject newFile
797     ) {
798         map.remove (item.primaryFile);
799         item.primaryFile = newFile;
800         map.put (newFile, item);
801         countRegistration(newFile);
802     }
803
804     /** When the loader pool is changed, then all objects are rescanned.
805     */

806     public void stateChanged (javax.swing.event.ChangeEvent JavaDoc ev) {
807         Set<Item> set;
808         synchronized (this) {
809             // copy the values synchronously
810
set = new HashSet<Item>(map.values());
811         }
812         revalidate(createSetOfAllFiles(set));
813     }
814     
815     /** Create list of all files for given collection of data objects.
816     * @param c collection of DataObjectPool.Item
817     * @return set of files
818     */

819     private static Set<FileObject> createSetOfAllFiles(Collection<Item> c) {
820         Set<FileObject> set = new HashSet<FileObject>(c.size() * 7);
821         
822         for (Item item : c) {
823             DataObject obj = item.getDataObjectOrNull ();
824             if (obj != null) {
825                 getPOOL ().waitNotified (obj);
826                 set.addAll (obj.files ());
827             }
828         }
829         return set;
830     }
831     
832     /** Returns all currently existing data
833     * objects.
834     */

835     Iterator<Item> getActiveDataObjects () {
836         synchronized (this) {
837             return new ArrayList<Item>(map.values()).iterator();
838         }
839     }
840
841     /** One item in object pool.
842     */

843     static final class Item extends Object JavaDoc {
844         /** initial value of obj field. */
845         private static final Reference<DataObject> REFERENCE_NOT_SET = new WeakReference<DataObject>(null);
846
847         /** weak reference data object with this primary file */
848         private Reference<DataObject> obj = REFERENCE_NOT_SET;
849         
850         /** primary file */
851         FileObject primaryFile;
852         
853         // [PENDING] hack to check the stack when the DataObject has been created
854
// private Exception stack;
855

856         /** @param fo primary file
857         * @param pool object pool
858         */

859         public Item (FileObject fo) {
860             this.primaryFile = fo;
861
862             // [PENDING] // stores stack
863
/* java.io.StringWriter sw = new java.io.StringWriter ();
864                   stack = new Exception ();
865                 }
866
867                 // [PENDING] toString returns original stack
868                 public String toString () {
869                   return stack.toString ();*/

870         }
871
872         /** Setter for the data object. Called immediatelly as possible.
873         * @param obj the data object for this item
874         */

875         public void setDataObject (DataObject obj) {
876             this.obj = new ItemReference (obj, this);
877             
878             if (obj != null && !obj.getPrimaryFile ().isValid ()) {
879                 // if the primary file is already invalid =>
880
// mark the object as invalid
881
deregister (false);
882             }
883             
884             synchronized (DataObjectPool.getPOOL()) {
885                 DataObjectPool.getPOOL().notifyAll();
886             }
887         }
888
889         /** Getter for the data object.
890         * @return the data object or null
891         */

892         DataObject getDataObjectOrNull () {
893             synchronized (DataObjectPool.getPOOL()) {
894                 while (this.obj == REFERENCE_NOT_SET) {
895                     try {
896                         DataObjectPool.getPOOL().wait ();
897                     }
898                     catch (InterruptedException JavaDoc exc) {
899                     }
900                 }
901             }
902             
903             return this.obj == null ? null : this.obj.get();
904         }
905         
906         /** Getter for the data object.
907         * @return the data object
908         * @exception IllegalStateException if the data object has been lost
909         * due to weak references (should not happen)
910         */

911         public DataObject getDataObject () {
912             DataObject obj = getDataObjectOrNull ();
913             if (obj == null) {
914                 throw new IllegalStateException JavaDoc ();
915             }
916             return obj;
917         }
918
919         /** Deregister one reference.
920         * @param refresh true if the parent folder should be refreshed
921         */

922         public void deregister (boolean refresh) {
923             getPOOL().deregister (this, refresh);
924         }
925
926         /** Changes the primary file to new one.
927         * @param newFile new primary file to set
928         */

929         public void changePrimaryFile (FileObject newFile) {
930             getPOOL().changePrimaryFile (this, newFile);
931         }
932
933         /** Is the item valid?
934         */

935         public boolean isValid () {
936             if (getPOOL().map.get (primaryFile) == this) {
937                 return primaryFile.isValid();
938             } else {
939                 return false;
940             }
941             
942         }
943         
944         public String JavaDoc toString () {
945             DataObject obj = this.obj.get ();
946             if (obj == null) {
947                 return "nothing[" + primaryFile + "]"; // NOI18N
948
}
949             return obj.toString ();
950         }
951     }
952
953     /** WeakReference - references a DataObject, strongly references an Item */
954     static final class ItemReference extends WeakReference<DataObject>
955     implements Runnable JavaDoc {
956         /** Reference to an Item */
957         private Item item;
958         
959         ItemReference(DataObject dobject, Item item) {
960             super(dobject, org.openide.util.Utilities.activeReferenceQueue());
961             this.item = item;
962         }
963
964         /** Does the cleanup of the reference */
965         public void run () {
966             item.deregister(false);
967             item = null;
968         }
969         
970     }
971     
972     /** Validator to allow rescan of files.
973     */

974     private static final class Validator extends Object JavaDoc
975     implements DataLoader.RecognizedFiles {
976         /** error manager to log what is happening here */
977         private static final Logger err = Logger.getLogger("org.openide.loaders.DataObject.Validator"); // NOI18N
978

979         /** set of all files that should be revalidated */
980         private Set<FileObject> files;
981         /** current thread that is in the validator */
982         private Thread JavaDoc current;
983         /** number of threads waiting to enter the validation */
984         private int waiters;
985         /** Number of calls to enter by current thread minus 1 */
986         private int reenterCount;
987         /** set of files that has been marked recognized */
988         private Set<FileObject> recognizedFiles;
989         /** set with all objects that refused to be discarded */
990         private Set<DataObject> refusingObjects;
991         /** set of files that has been registered during revalidation */
992         private Set<FileObject> createdFiles;
993
994     Validator() {}
995
996         /** Enters the section.
997         * @param set mutable set of files that should be processed
998         * @return the set of files concatenated with any previous sets
999         */

1000        private synchronized Set<FileObject> enter(Set<FileObject> set) {
1001            boolean log = err.isLoggable (Level.FINE);
1002            if (log) {
1003                err.fine("enter: " + set + " on thread: " + Thread.currentThread ()); // NOI18N
1004
}
1005            if (current == Thread.currentThread ()) {
1006                reenterCount++;
1007                if (log) {
1008                    err.fine("current thread, rentered: " + reenterCount); // NOI18N
1009
}
1010            } else {
1011                waiters++;
1012                if (log) {
1013                    err.fine("Waiting as waiter: " + waiters); // NOI18N
1014
}
1015                while (current != null) {
1016                    try {
1017                        wait ();
1018                    } catch (InterruptedException JavaDoc ex) {
1019                    }
1020                }
1021                current = Thread.currentThread ();
1022                waiters--;
1023                if (log) {
1024                    err.fine("Wait finished, waiters: " + waiters + " new current: " + current); // NOI18N
1025
}
1026            }
1027            
1028            if (files == null) {
1029                if (log) {
1030                    err.fine("New files: " + set); // NOI18N
1031
}
1032                files = set;
1033            } else {
1034                files.addAll (set);
1035                if (log) {
1036                    err.fine("Added files: " + set); // NOI18N
1037
err.fine("So they are: " + files); // NOI18N
1038
}
1039            }
1040
1041            return files;
1042        }
1043
1044        /** Leaves the critical section.
1045        */

1046        private synchronized void exit () {
1047            boolean log = err.isLoggable (Level.FINE);
1048            if (reenterCount == 0) {
1049                current = null;
1050                if (waiters == 0) {
1051                    files = null;
1052                }
1053                notify ();
1054                if (log) {
1055                    err.fine("Exit and notify from " + Thread.currentThread ()); // NOI18N
1056
}
1057            } else {
1058                reenterCount--;
1059                if (log) {
1060                    err.fine("Exit reentrant: " + reenterCount); // NOI18N
1061
}
1062            }
1063        }
1064
1065        /** If there is another waiting thread, then I can
1066        * cancel my computation.
1067        */

1068        private synchronized boolean goOn () {
1069            return waiters == 0;
1070        }
1071
1072        /** Called to either refresh folder, or register the folder to be
1073        * refreshed later is validation is in progress.
1074        */

1075        public void refreshFolderOf (DataFolder df) {
1076            if (createdFiles == null) {
1077                // no validator in progress
1078
FolderList.changedDataSystem (df.getPrimaryFile ());
1079            }
1080        }
1081
1082        /** Mark this file as being recognized. It will be excluded
1083        * from further processing.
1084        *
1085        * @param fo file object to exclude
1086        */

1087        public void markRecognized (FileObject fo) {
1088            recognizedFiles.add (fo);
1089        }
1090
1091        public void notifyRegistered (FileObject fo) {
1092            if (createdFiles != null) {
1093                createdFiles.add (fo);
1094            }
1095        }
1096
1097        /** Reregister new object for already existing file object.
1098        * @param obj old object existing
1099        * @param loader loader of new object to create
1100        * @return true if the old object has been discarded and new one can
1101        * be created
1102        */

1103        public boolean reregister (DataObject obj, DataLoader loader) {
1104            if (recognizedFiles == null) {
1105                // revalidation not in progress
1106
return false;
1107            }
1108
1109            if (obj.getLoader () == loader) {
1110                // no change in loader =>
1111
return false;
1112            }
1113
1114            if (createdFiles.contains (obj.getPrimaryFile ())) {
1115                // if the file already has been created
1116
return false;
1117            }
1118
1119            if (refusingObjects.contains (obj)) {
1120                // the object has been refused before
1121
return false;
1122            }
1123
1124            return true;
1125        }
1126
1127        /** Rescans all fileobjects in given set.
1128        * @param s mutable set of FileObjects
1129        * @return set of objects that refused to be revalidated
1130        */

1131        public Set<DataObject> revalidate (Set<FileObject> s) {
1132            
1133            // ----------------- fix of #30559 START
1134
if ((s.size() == 1) && (current == Thread.currentThread ())) {
1135                if (files != null && files.contains(s.iterator().next())) {
1136                    return new HashSet<DataObject>();
1137                }
1138            }
1139            // ----------------- fix of #30559 END
1140

1141            // holds all created object, so they are not garbage
1142
// collected till this method ends
1143
List<DataObject> createObjects = new LinkedList<DataObject>();
1144            boolean log = err.isLoggable (Level.FINE);
1145            try {
1146                
1147                s = enter (s);
1148                
1149                recognizedFiles = new HashSet<FileObject>();
1150                refusingObjects = new HashSet<DataObject>();
1151                createdFiles = new HashSet<FileObject>();
1152
1153                DataLoaderPool pool = lp;
1154                Iterator<FileObject> it = s.iterator();
1155                while (it.hasNext () && goOn ()) {
1156                    try {
1157                        FileObject fo = it.next();
1158                        if (log) {
1159                            err.fine("Iterate: " + fo); // NOI18N
1160
}
1161                        
1162                        if (!recognizedFiles.contains (fo)) {
1163                            // first of all test if the file is on a valid filesystem
1164
boolean invalidate = false;
1165
1166                            // the previous data object should be canceled
1167
DataObject orig = getPOOL().find (fo);
1168                            if (log) {
1169                                err.fine("Original: " + orig); // NOI18N
1170
}
1171                            if (orig == null) {
1172                                // go on
1173
continue;
1174                            }
1175
1176                            // findDataObject
1177
// is not using method DataObjectPool.find to locate data object
1178
// directly for primary file, that is good
1179
DataObject obj = pool.findDataObject (fo, this);
1180                            createObjects.add (obj);
1181
1182                            invalidate = obj != orig;
1183
1184                            if (invalidate) {
1185                                if (log) {
1186                                    err.fine("Invalidate: " + obj); // NOI18N
1187
}
1188                                it.remove();
1189                                try {
1190                                    orig.setValid (false);
1191                                } catch (java.beans.PropertyVetoException JavaDoc ex) {
1192                                    refusingObjects.add (orig);
1193                                    if (log) {
1194                                        err.fine(" Refusing: " + orig); // NOI18N
1195
}
1196                                }
1197                            }
1198                        }
1199                    } catch (DataObjectExistsException ex) {
1200                        // this should be no problem here
1201
} catch (java.io.IOException JavaDoc ioe) {
1202                        Logger.getLogger(DataObjectPool.class.getName()).log(Level.WARNING, null, ioe);
1203                    } catch (ConcurrentModificationException cme) {
1204                        // not very nice but the only way I could come up to handle this:
1205
// java.util.ConcurrentModificationException
1206
// at java.util.HashMap$HashIterator.remove(HashMap.java:755)
1207
// at org.openide.loaders.DataObjectPool$Validator.revalidate(DataObjectPool.java:916)
1208
// at org.openide.loaders.DataObjectPool.revalidate(DataObjectPool.java:203)
1209
// at org.openide.loaders.DataObjectPool.stateChanged(DataObjectPool.java:527)
1210
// at org.openide.loaders.DataLoaderPool$1.run(DataLoaderPool.java:128)
1211
// at org.openide.util.Task.run(Task.java:136)
1212
//[catch] at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:635)
1213
// is to ignore the exception and continue
1214
it = s.iterator();
1215                        if (log) {
1216                            err.log(Level.FINE, null, cme);
1217                            err.fine("New iterator over: " + s); // NOI18N
1218
}
1219                    }
1220                }
1221                return refusingObjects;
1222            } finally {
1223                recognizedFiles = null;
1224                refusingObjects = null;
1225                createdFiles = null;
1226
1227                exit ();
1228
1229                if (log) {
1230                    err.fine("will do refreshAllFolders: "+ s.size ()); // NOI18N
1231
}
1232                
1233                getPOOL().refreshAllFolders ();
1234                
1235                if (log) {
1236                    err.fine("refreshAllFolders done"); // NOI18N
1237
}
1238            }
1239        }
1240        
1241    } // end of Validator
1242
}
1243
Popular Tags