--- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEConfigurationProvider.java Thu Sep 04 14:05:33 2008 +0200 +++ a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEConfigurationProvider.java Sat Sep 06 23:44:22 2008 +0200 @@ -103,35 +103,16 @@ private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private final FileChangeListener fcl = new FileChangeAdapter() { public void fileFolderCreated(FileEvent fe) { - update(fe); + updateConfigurations(fe); } public void fileDataCreated(FileEvent fe) { - update(fe); + updateConfigurations(fe); } public void fileDeleted(FileEvent fe) { - update(fe); + updateConfigurations(fe); } public void fileRenamed(FileRenameEvent fe) { - update(fe); - } - private void update(FileEvent ev) { - LOGGER.log(Level.FINEST, "Received {0}", ev); - Set oldConfigs = configs != null ? configs.keySet() : Collections.emptySet(); - configDir = p.getProjectDirectory().getFileObject("nbproject/configs"); // NOI18N - if (configDir != null) { - configDir.removeFileChangeListener(fclWeak); - configDir.addFileChangeListener(fclWeak); - LOGGER.log(Level.FINEST, "(Re-)added listener to {0}", configDir); - } else { - LOGGER.log(Level.FINEST, "No nbproject/configs exists"); - } - calculateConfigs(); - Set newConfigs = configs.keySet(); - if (!oldConfigs.equals(newConfigs)) { - LOGGER.log(Level.FINER, "Firing " + ProjectConfigurationProvider.PROP_CONFIGURATIONS + ": {0} -> {1}", new Object[] {oldConfigs, newConfigs}); - pcs.firePropertyChange(ProjectConfigurationProvider.PROP_CONFIGURATIONS, null, null); - // XXX also fire PROP_ACTIVE_CONFIGURATION? - } + updateConfigurations(fe); } }; private final FileChangeListener fclWeak; @@ -155,11 +136,31 @@ p.evaluator().addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if (PROP_CONFIG.equals(evt.getPropertyName())) { + updateConfigurations(null); LOGGER.log(Level.FINER, "Refiring " + PROP_CONFIG + " -> " + ProjectConfigurationProvider.PROP_CONFIGURATION_ACTIVE); pcs.firePropertyChange(ProjectConfigurationProvider.PROP_CONFIGURATION_ACTIVE, null, null); } } }); + } + private void updateConfigurations(FileEvent ev) { + LOGGER.log(Level.FINEST, "Received {0}", ev); + Set oldConfigs = configs != null ? configs.keySet() : Collections.emptySet(); + configDir = p.getProjectDirectory().getFileObject("nbproject/configs"); // NOI18N + if (configDir != null) { + configDir.removeFileChangeListener(fclWeak); + configDir.addFileChangeListener(fclWeak); + LOGGER.log(Level.FINEST, "(Re-)added listener to {0}", configDir); + } else { + LOGGER.log(Level.FINEST, "No nbproject/configs exists"); + } + calculateConfigs(); + Set newConfigs = configs.keySet(); + if (!oldConfigs.equals(newConfigs)) { + LOGGER.log(Level.FINER, "Firing " + ProjectConfigurationProvider.PROP_CONFIGURATIONS + ": {0} -> {1}", new Object[] {oldConfigs, newConfigs}); + pcs.firePropertyChange(ProjectConfigurationProvider.PROP_CONFIGURATIONS, null, null); + // XXX also fire PROP_ACTIVE_CONFIGURATION? + } } private void calculateConfigs() { --- a/java.j2seproject/test/unit/src/org/netbeans/modules/java/j2seproject/J2SEConfigurationProviderTest.java Thu Sep 04 14:05:33 2008 +0200 +++ a/java.j2seproject/test/unit/src/org/netbeans/modules/java/j2seproject/J2SEConfigurationProviderTest.java Sat Sep 06 23:44:22 2008 +0200 @@ -230,8 +230,6 @@ return null; } }); - //todo: workaround, fix me! - Thread.sleep(1000); ProjectManager.mutex().readAccess(new Mutex.ExceptionAction() { public Void run () throws Exception { assertEquals(new HashSet(Arrays.asList(ProjectConfigurationProvider.PROP_CONFIGURATIONS, ProjectConfigurationProvider.PROP_CONFIGURATION_ACTIVE)), @@ -243,6 +241,30 @@ }); } + public void testInitialListeningInOneSection() throws Exception { // #84781 + final TestListener l = new TestListener(); + ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() { + public Void run() throws Exception { + pcp.addPropertyChangeListener(l); + EditableProperties props = new EditableProperties(); + props.setProperty("$label", "X"); + write(props, d, "nbproject/configs/x.properties"); + EditableProperties p2 = new EditableProperties(); + p2.setProperty("config", "x"); + write(p2, d, "nbproject/private/config.properties"); + assertEquals(new HashSet(Arrays.asList(ProjectConfigurationProvider.PROP_CONFIGURATIONS, ProjectConfigurationProvider.PROP_CONFIGURATION_ACTIVE)), + l.events()); + assertEquals(2, pcp.getConfigurations().size()); + assertEquals("X", pcp.getActiveConfiguration().getDisplayName()); + return null; + } + }); + } + + private void write(EditableProperties p, FileObject d, String path) throws IOException { + FileObject f = FileUtil.createData(d, path); + p.store(f); + } private void write(Properties p, FileObject d, String path) throws IOException { FileObject f = FileUtil.createData(d, path); OutputStream os = f.getOutputStream(); --- a/project.ant/manifest.mf Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/manifest.mf Sat Sep 06 23:44:22 2008 +0200 @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.project.ant/1 -OpenIDE-Module-Specification-Version: 1.25 +OpenIDE-Module-Specification-Version: 1.26 OpenIDE-Module-Layer: org/netbeans/modules/project/ant/resources/mf-layer.xml OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/project/ant/Bundle.properties OpenIDE-Module-Install: org/netbeans/modules/project/ant/AntProjectModule.class --- a/project.ant/src/org/netbeans/api/project/ant/AntBuildExtender.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/src/org/netbeans/api/project/ant/AntBuildExtender.java Sat Sep 06 23:44:22 2008 +0200 @@ -272,21 +272,9 @@ propValue.delete(propValue.length() - 1, propValue.length()); editableProps.setProperty(propName, propValue.toString()); - - OutputStream os = null; - FileLock lock = null; - try { - lock = projPropsFO.lock(); - os = projPropsFO.getOutputStream(lock); - editableProps.store(os); - } finally { - if (lock != null) { - lock.releaseLock(); - } - if (os != null) { - os.close(); - } - } + + + editableProps.store(projPropsFO); return null; } }); --- a/project.ant/src/org/netbeans/modules/project/ant/FileChangeSupport.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/src/org/netbeans/modules/project/ant/FileChangeSupport.java Sat Sep 06 23:44:22 2008 +0200 @@ -48,6 +48,10 @@ import java.lang.ref.WeakReference; import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; import org.openide.filesystems.FileChangeListener; import org.openide.filesystems.FileAttributeEvent; import org.openide.filesystems.FileEvent; @@ -112,12 +116,49 @@ f2H.remove(path); } } + + public void notifyChange(File f) { + Set fireTo = new LinkedHashSet(); + synchronized (holders) { + for (Map.Entry> e : holders.entrySet()) { + Holder h = e.getValue().get(f); + if (h != null) { + fireTo.add(h); + } + h = e.getValue().get(f.getParentFile()); + if (h != null) { + fireTo.add(h); + } + } + } + if (fireTo.isEmpty()) { + return; + } + long now = System.currentTimeMillis(); + for (Holder holder : fireTo) { + holder.last = now; + } + FileObject who = FileUtil.toFileObject(f); + for (Holder holder : fireTo) { + holder.toNotify = true; + } + if (who != null) { + who.refresh(true); + } + for (Holder holder : fireTo) { + if (holder.toNotify) { + holder.someChange(System.currentTimeMillis(), null, who); + } + } + } private static final class Holder extends WeakReference implements FileChangeListener, Runnable { private final File path; private FileObject current; private File currentF; + private long last; + boolean toNotify; public Holder(FileChangeSupportListener listener, File path) { super(listener, Utilities.activeReferenceQueue()); @@ -158,7 +199,13 @@ } } - private void someChange(FileObject modified) { + private void someChange(long time, FileEvent orig, FileObject modified) { + if (orig != null && orig.getTime() < last) { + return; + } + last = time; + toNotify = false; + FileChangeSupportListener listener; FileObject oldCurrent, nueCurrent; File oldCurrentF, nueCurrentF; @@ -177,39 +224,39 @@ nueCurrentF = currentF; } if (modified != null && modified == nueCurrent) { - FileChangeSupportEvent event = new FileChangeSupportEvent(DEFAULT, FileChangeSupportEvent.EVENT_MODIFIED, path); + FileChangeSupportEvent event = new FileChangeSupportEvent(orig, DEFAULT, FileChangeSupportEvent.EVENT_MODIFIED, path); listener.fileModified(event); } else { boolean oldWasCorrect = path.equals(oldCurrentF); boolean nueIsCorrect = path.equals(nueCurrentF); if (oldWasCorrect && !nueIsCorrect) { - FileChangeSupportEvent event = new FileChangeSupportEvent(DEFAULT, FileChangeSupportEvent.EVENT_DELETED, path); + FileChangeSupportEvent event = new FileChangeSupportEvent(orig, DEFAULT, FileChangeSupportEvent.EVENT_DELETED, path); listener.fileDeleted(event); } else if (nueIsCorrect && !oldWasCorrect) { - FileChangeSupportEvent event = new FileChangeSupportEvent(DEFAULT, FileChangeSupportEvent.EVENT_CREATED, path); + FileChangeSupportEvent event = new FileChangeSupportEvent(orig, DEFAULT, FileChangeSupportEvent.EVENT_CREATED, path); listener.fileCreated(event); } } } public void fileChanged(FileEvent fe) { - someChange(fe.getFile()); + someChange(0, fe, fe.getFile()); } public void fileDeleted(FileEvent fe) { - someChange(null); + someChange(0, fe, null); } public void fileDataCreated(FileEvent fe) { - someChange(null); + someChange(0, fe, null); } public void fileFolderCreated(FileEvent fe) { - someChange(null); + someChange(0, fe, null); } public void fileRenamed(FileRenameEvent fe) { - someChange(null); + someChange(0, fe, null); } public void fileAttributeChanged(FileAttributeEvent fe) { --- a/project.ant/src/org/netbeans/modules/project/ant/FileChangeSupportEvent.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/src/org/netbeans/modules/project/ant/FileChangeSupportEvent.java Sat Sep 06 23:44:22 2008 +0200 @@ -44,6 +44,7 @@ import java.util.EventObject; +import org.openide.filesystems.FileEvent; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; @@ -60,11 +61,13 @@ private final int type; private final File path; + private final FileEvent original; - FileChangeSupportEvent(FileChangeSupport support, int type, File path) { + FileChangeSupportEvent(FileEvent orig, FileChangeSupport support, int type, File path) { super(support); this.type = type; this.path = path; + this.original = orig; } public int getType() { @@ -78,7 +81,12 @@ public FileObject getFileObject() { return FileUtil.toFileObject(path); } + + public FileEvent getOriginal() { + return original; + } + @Override public String toString() { return "FCSE[" + "CDM".charAt(type) + ":" + path + "]"; // NOI18N } --- a/project.ant/src/org/netbeans/modules/project/ant/ProjectLibraryProvider.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/src/org/netbeans/modules/project/ant/ProjectLibraryProvider.java Sat Sep 06 23:44:22 2008 +0200 @@ -752,12 +752,7 @@ ep.remove(key); } FileObject fo = FileUtil.createData(propfile); - OutputStream os = fo.getOutputStream(); - try { - ep.store(os); - } finally { - os.close(); - } + ep.store(fo); } static final class ProjectLibraryArea implements LibraryStorageArea { --- a/project.ant/src/org/netbeans/spi/project/support/ant/EditableProperties.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/src/org/netbeans/spi/project/support/ant/EditableProperties.java Sat Sep 06 23:44:22 2008 +0200 @@ -60,6 +60,11 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; +import org.netbeans.modules.project.ant.FileChangeSupport; +import org.openide.filesystems.FileLock; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileSystem.AtomicAction; +import org.openide.filesystems.FileUtil; // XXX: consider adding getInitialComment() and setInitialComment() methods // (useful e.g. for GeneratedFilesHelper) @@ -230,6 +235,35 @@ } } output.flush(); + } + + /** + * Store properties to a file object. Helper method that deals with opening + * and closing of the file object. + * @param fo file object to store the stream to + * @throws IOException if the stream could not be written to + * @since 1.26 + */ + public void store(final FileObject fo) throws IOException { + FileUtil.runAtomicAction(new AtomicAction() { + public void run() throws IOException { + OutputStream os = null; + FileLock lock = null; + try { + lock = fo.lock(); + os = fo.getOutputStream(lock); + store(os); + } finally { + if (lock != null) { + lock.releaseLock(); + } + if (os != null) { + os.close(); + } + } + FileChangeSupport.DEFAULT.notifyChange(FileUtil.toFile(fo)); + } + }); } @Override --- a/project.ant/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelper.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelper.java Sat Sep 06 23:44:22 2008 +0200 @@ -354,22 +354,11 @@ // Try to acquire both locks together, since we need them both written. FileLock lock1 = buildScriptXml.lock(); try { - FileLock lock2 = _genfiles.lock(); + OutputStream os1 = new EolFilterOutputStream(buildScriptXml.getOutputStream(lock1)); try { - OutputStream os1 = new EolFilterOutputStream(buildScriptXml.getOutputStream(lock1)); - try { - OutputStream os2 = _genfiles.getOutputStream(lock2); - try { - os1.write(resultData); - p.store(os2); - } finally { - os2.close(); - } - } finally { - os1.close(); - } + p.store(_genfiles); } finally { - lock2.releaseLock(); + os1.close(); } } finally { lock1.releaseLock(); --- a/project.ant/src/org/netbeans/spi/project/support/ant/ProjectProperties.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/src/org/netbeans/spi/project/support/ant/ProjectProperties.java Sat Sep 06 23:44:22 2008 +0200 @@ -207,7 +207,7 @@ } else { properties = null; } - fireChange(); + fireChange(null); } return modifying; } @@ -282,7 +282,7 @@ private void reload() { helper.cancelPendingHook(); // Revert the save. - diskChange(); + diskChange(null); } }); } @@ -323,7 +323,7 @@ cs.removeChangeListener(l); } - private void fireChange() { + private void fireChange(FileChangeSupportEvent ev) { if (!cs.hasListeners()) { return; } @@ -333,6 +333,11 @@ return null; } }; + if (ev != null && ev.getOriginal() != null && ProjectManager.firedWithMutexAccess(ev.getOriginal())) { + ProjectManager.mutex().readAccess(action); + return; + } + if (ProjectManager.mutex().isWriteAccess()) { // Run it right now. postReadRequest would be too late. ProjectManager.mutex().readAccess(action); @@ -349,27 +354,27 @@ } } - private void diskChange() { + private void diskChange(FileChangeSupportEvent ev) { // XXX should check for a possible clobber from in-memory data if (!writing) { loaded = false; } - fireChange(); + fireChange(ev); if (!writing) { helper.fireExternalChange(path); } } public void fileCreated(FileChangeSupportEvent event) { - diskChange(); + diskChange(event); } public void fileDeleted(FileChangeSupportEvent event) { - diskChange(); + diskChange(event); } public void fileModified(FileChangeSupportEvent event) { - diskChange(); + diskChange(event); } } --- a/project.ant/src/org/netbeans/spi/project/support/ant/PropertyUtils.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/src/org/netbeans/spi/project/support/ant/PropertyUtils.java Sat Sep 06 23:44:22 2008 +0200 @@ -175,17 +175,7 @@ return null; } } - FileLock lock = bp.lock(); - try { - OutputStream os = bp.getOutputStream(lock); - try { - properties.store(os); - } finally { - os.close(); - } - } finally { - lock.releaseLock(); - } + properties.store(bp); } else { throw new IOException("Do not know where to store build.properties; must set netbeans.user!"); // NOI18N } --- a/project.ant/test/unit/src/org/netbeans/modules/project/ant/FileChangeSupportTest.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/test/unit/src/org/netbeans/modules/project/ant/FileChangeSupportTest.java Sat Sep 06 23:44:22 2008 +0200 @@ -48,13 +48,19 @@ import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InterruptedIOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import junit.framework.Test; import org.netbeans.api.project.TestUtil; import org.netbeans.junit.NbTestCase; +import org.netbeans.junit.NbTestSuite; import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileSystem.AtomicAction; import org.openide.filesystems.FileUtil; +import org.openide.util.Exceptions; /** * Test {@link FileChangeSupport}. @@ -76,7 +82,18 @@ private FileObject scratch; private String scratchPath; + + + public static Test suite() { + Test t = null; +// t = new FileChangeSupportTest("testDirectNotifyChanges"); + if (t == null) { + t = new NbTestSuite(FileChangeSupportTest.class); + } + return t; + } + @Override protected void setUp() throws Exception { super.setUp(); scratch = TestUtil.makeScratchDir(this); @@ -152,6 +169,48 @@ scratch.getFileSystem().refresh(false); assertEquals("and then a file deletion event", Collections.singletonList("D:" + fileF), l.check()); } + + public void testDirectNotifyChanges() throws Exception { + doDirectChanges(); + } + + public void testDirectNotifyChangesInAtomicAction() throws Exception { + scratch.getFileSystem().runAtomicAction(new AtomicAction() { + public void run() throws IOException { + doDirectChanges(); + } + }); + } + + + private void doDirectChanges() throws IOException { + File fileF = new File(scratchPath + SEP + "dir" + SEP + "file2"); + L l = new L(); + FileChangeSupport.DEFAULT.addListener(l, fileF); + File dirF = new File(scratchPath + SEP + "dir"); + dirF.mkdir(); + new FileOutputStream(fileF).close(); + FileChangeSupport.DEFAULT.notifyChange(fileF); + assertEquals("got file creation event", Collections.singletonList("M:" + fileF), l.check()); + scratch.getFileSystem().refresh(false); + assertEquals("no new file creation event", Collections.emptyList(), l.check()); + try { + Thread.sleep(SLEEP); // make sure timestamp changes + } catch (InterruptedException ex) { + throw new InterruptedIOException(ex.getMessage()); + } + new FileOutputStream(fileF).close(); + FileChangeSupport.DEFAULT.notifyChange(fileF); + assertEquals("and then a mod in file", Collections.singletonList("M:" + fileF), l.check()); + scratch.getFileSystem().refresh(false); + assertEquals("no new file mod event", Collections.emptyList(), l.check()); + fileF.delete(); + dirF.delete(); + FileChangeSupport.DEFAULT.notifyChange(fileF); + assertEquals("and then a file deletion event", Collections.singletonList("D:" + fileF), l.check()); + scratch.getFileSystem().refresh(false); + assertEquals("no new file del w file mod event", Collections.emptyList(), l.check()); + } public void test66444() throws Exception { File fileF = new File(scratchPath + SEP + "dir" + SEP + "file2"); --- a/project.ant/test/unit/src/org/netbeans/modules/project/ant/ProjectLibraryProviderTest.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/test/unit/src/org/netbeans/modules/project/ant/ProjectLibraryProviderTest.java Sat Sep 06 23:44:22 2008 +0200 @@ -391,9 +391,7 @@ String[] nameValue = def.split("=", 2); ep.put(nameValue[0], nameValue[1]); } - OutputStream os = f.getOutputStream(); - ep.store(os); - os.close(); + ep.store(f); } private static void storeDefs(Project project, String... definitions) throws IOException { --- a/project.ant/test/unit/src/org/netbeans/spi/project/support/ant/EditablePropertiesTest.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/test/unit/src/org/netbeans/spi/project/support/ant/EditablePropertiesTest.java Sat Sep 06 23:44:22 2008 +0200 @@ -59,6 +59,14 @@ import java.util.Iterator; import java.util.Map; import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.project.ant.FileChangeSupport; +import org.netbeans.modules.project.ant.FileChangeSupportEvent; +import org.netbeans.modules.project.ant.FileChangeSupportListener; +import org.openide.filesystems.FileChangeAdapter; +import org.openide.filesystems.FileEvent; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileSystem.AtomicAction; +import org.openide.filesystems.FileUtil; public class EditablePropertiesTest extends NbTestCase { @@ -121,6 +129,56 @@ String dest = getWorkDirPath()+File.separatorChar+"new.properties"; saveProperties(ep, dest); assertFile("Saved properties must be the same as original one", filenameOfTestProperties(), dest, (String)null); + } + + public void testSaveToFileObject() throws Exception { + doSaveToFileObject(); + } + + public void testSaveToFileObjectInAtomic() throws Exception { + final Add[] ret = new Add[1]; + + FileUtil.runAtomicAction(new AtomicAction() { + public void run() throws IOException { + ret[0] = doSaveToFileObject(); + } + }); + + assertEquals("No additional events delivered after atomic action", 1, ret[0].cnt); + } + private static class Add implements FileChangeSupportListener { + int cnt; + + public void fileCreated(FileChangeSupportEvent event) { + fail(); + } + + public void fileDeleted(FileChangeSupportEvent event) { + fail(); + } + + public void fileModified(FileChangeSupportEvent event) { + cnt++; + } + } + + private Add doSaveToFileObject() throws IOException { + clearWorkDir(); + EditableProperties ep = loadTestProperties(); + FileObject some = FileUtil.toFileObject(getWorkDir()).createData("p.properties"); + some.refresh(); + + File destFile = FileUtil.toFile(some); + String dest = destFile.getPath(); + + Add listener = new Add(); + FileChangeSupport.DEFAULT.addListener(listener, destFile); + + ep.store(some); + assertFile("Saved properties must be the same as original one", filenameOfTestProperties(), dest, (String)null); + assertEquals("One change notified", 1, listener.cnt); + + return listener; } public void testClonability() throws Exception { --- a/project.ant/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java Thu Sep 04 14:05:33 2008 +0200 +++ a/project.ant/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java Sat Sep 06 23:44:22 2008 +0200 @@ -979,18 +979,8 @@ for (int cntr = 0; cntr < keys.length; cntr++) { p.setProperty(keys[cntr], values[cntr]); } - - FileLock lock = prop.lock(); - try { - OutputStream os = prop.getOutputStream(lock); - try { - p.store(os); - } finally { - os.close(); - } - } finally { - lock.releaseLock(); - } + + p.store(prop); } public void testFixReferences() throws Exception { --- a/projectapi/src/org/netbeans/api/project/ProjectManager.java Thu Sep 04 14:05:33 2008 +0200 +++ a/projectapi/src/org/netbeans/api/project/ProjectManager.java Sat Sep 06 23:44:22 2008 +0200 @@ -112,19 +112,43 @@ return DEFAULT; } - private static final Executor FS_EXEC = new Executor() { - public void execute(final Runnable command) { + private static final PostCommand FS_EXEC = new PostCommand(); + + private static class PostCommand implements Executor, FileSystem.AtomicAction { + private Runnable run; + + public void execute(Runnable run) { + PostCommand p = new PostCommand(); + p.run = run; try { - FileUtil.runAtomicAction(new FileSystem.AtomicAction() { - public void run() throws IOException { - command.run(); - } - }); + FileUtil.runAtomicAction(p); } catch (IOException ex) { throw (IllegalStateException) new IllegalStateException().initCause(ex); } } - }; + + public void run() throws IOException { + run.run(); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + return obj.getClass().equals(getClass()); + } + } + + // XXX: apichange, maybe test in this module + public static boolean firedWithMutexAccess(FileEvent ev) { + return ev.firedFrom(FS_EXEC); + } private static final Mutex MUTEX = new Mutex(new Mutex.Privileged(), FS_EXEC);