# This patch file was generated by NetBeans IDE # Following Index: paths are relative to: /data/work/src/netbeans-cdev # This patch can be applied using context Tools: Patch action on respective folder. # It uses platform neutral UTF-8 encoding and \n newlines. # Above lines and this line are ignored by the patching process. Index: masterfs/apichanges.xml --- masterfs/apichanges.xml Base (BASE) +++ masterfs/apichanges.xml Locally Modified (Based On LOCAL) @@ -49,6 +49,31 @@ MasterFileSystem API + + + Guaranteed event order when deleting and creating files and new calls in ProvidedExtensions + + + + + +

+

    +
  • + Guaranteed event order when deleting and creating files in atomic action - + createSuccess and deleteSuccess are called imediatelly + after a file is created or deleted. +
  • +
  • new calls createdExternally, deletedExternally, + fileChanged, beforeMove, + moveSuccess, moveFailure +
  • +
+

+
+ + +
ProvidedExtensions.priorityIO to suspend background refresh Index: masterfs/manifest.mf --- masterfs/manifest.mf Base (BASE) +++ masterfs/manifest.mf Locally Modified (Based On LOCAL) @@ -1,7 +1,7 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.masterfs/2 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/masterfs/resources/Bundle.properties -OpenIDE-Module-Specification-Version: 2.26 +OpenIDE-Module-Specification-Version: 2.27 AutoUpdate-Show-In-Client: false AutoUpdate-Essential-Module: true Index: masterfs/src/org/netbeans/modules/masterfs/ProvidedExtensionsProxy.java --- masterfs/src/org/netbeans/modules/masterfs/ProvidedExtensionsProxy.java Base (BASE) +++ masterfs/src/org/netbeans/modules/masterfs/ProvidedExtensionsProxy.java Locally Modified (Based On LOCAL) @@ -296,6 +296,96 @@ return 0; } + @Override + public void createdExternally(final FileObject fo) { + for (Iterator it = annotationProviders.iterator(); it.hasNext();) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener instanceof ProvidedExtensions) { + runCheckCode(new Runnable() { + public void run() { + ((ProvidedExtensions)iListener).createdExternally(fo); + } + }); + } + } + } + + @Override + public void deletedExternally(final FileObject fo) { + for (Iterator it = annotationProviders.iterator(); it.hasNext();) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener instanceof ProvidedExtensions) { + runCheckCode(new Runnable() { + public void run() { + ((ProvidedExtensions)iListener).deletedExternally(fo); + } + }); + } + } + } + + @Override + public void fileChanged(final FileObject fo) { + for (Iterator it = annotationProviders.iterator(); it.hasNext();) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener instanceof ProvidedExtensions) { + runCheckCode(new Runnable() { + public void run() { + ((ProvidedExtensions)iListener).fileChanged(fo); + } + }); + } + } + } + + @Override + public void beforeMove(final FileObject from, final File to) { + for (Iterator it = annotationProviders.iterator(); it.hasNext();) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener != null) { + runCheckCode(new Runnable() { + public void run() { + iListener.beforeMove(from, to); + } + }); + } + } + } + + @Override + public void moveSuccess(final FileObject from, final File to) { + for (Iterator it = annotationProviders.iterator(); it.hasNext();) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener != null) { + runCheckCode(new Runnable() { + public void run() { + iListener.moveSuccess(from, to); + } + }); + } + } + } + + @Override + public void moveFailure(final FileObject from, final File to) { + for (Iterator it = annotationProviders.iterator(); it.hasNext();) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener != null) { + runCheckCode(new Runnable() { + public void run() { + iListener.moveFailure(from, to); + } + }); + } + } + } + public static void checkReentrancy() { if (reentrantCheck.get() != null) { Logger.getLogger("org.netbeans.modules.masterfs.ProvidedExtensionsProxy").log(Level.INFO,"Unexpected reentrant call", new Throwable());//NOI18N Index: masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/BaseFileObj.java --- masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/BaseFileObj.java Base (BASE) +++ masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/BaseFileObj.java Locally Modified (Based On LOCAL) @@ -82,8 +82,6 @@ //constants private static final String PATH_SEPARATOR = File.separator;//NOI18N private static final char EXT_SEP = '.';//NOI18N - private FileChangeListener versioningWeakListener; - private final FileChangeListener versioningListener = new FileChangeListenerForVersioning(); //static fields static final long serialVersionUID = -1244650210876356809L; @@ -104,9 +102,6 @@ protected BaseFileObj(final File file, final FileNaming name) { this.fileName = name; - versioningWeakListener = WeakListeners.create(FileChangeListener.class, FileChangeListener.class, versioningListener, this); - addFileChangeListener(versioningWeakListener); - } @Override @@ -217,13 +212,17 @@ @Override public final FileObject move(FileLock lock, FileObject target, String name, String ext) throws IOException { + ProvidedExtensions extensions = getProvidedExtensions(); + File to = (target instanceof FolderObj) ? new File(((BaseFileObj) target).getFileName().getFile(), FileInfo.composeName(name, ext)) : + new File(FileUtil.toFile(target), FileInfo.composeName(name, ext)); + + extensions.beforeMove(this, to); + FileObject result = null; + try { if (!checkLock(lock)) { FSException.io("EXC_InvalidLock", lock, getPath()); // NOI18N } - ProvidedExtensions extensions = getProvidedExtensions(); - FileObject result = null; - File to = (target instanceof FolderObj) ? new File(((BaseFileObj) target).getFileName().getFile(), FileInfo.composeName(name, ext)) : - new File(FileUtil.toFile(target), FileInfo.composeName(name, ext)); + final IOHandler moveHandler = extensions.getMoveHandler(getFileName().getFile(), to); if (moveHandler != null) { if (target instanceof FolderObj) { @@ -242,6 +241,11 @@ } FileUtil.copyAttributes(this, result); + } catch (IOException ioe) { + extensions.moveFailure(this, to); + throw ioe; + } + extensions.moveSuccess(this, to); return result; } @@ -528,6 +532,8 @@ } public final void fireFileChangedEvent(final boolean expected) { + getProvidedExtensions().fileChanged(this); + Statistics.StopWatch stopWatch = Statistics.getStopWatch(Statistics.LISTENERS_CALLS); stopWatch.start(); @@ -617,6 +623,7 @@ getProvidedExtensions().deleteFailure(BaseFileObj.this); throw iex; } + getProvidedExtensions().deleteSuccess(BaseFileObj.this); return true; } }; @@ -708,6 +715,7 @@ } setValid(false); if (fire) { + getProvidedExtensions().deletedExternally(this); fireFileDeletedEvent(expected); } } @@ -865,32 +873,6 @@ return getExistingParentFor(getFileName().getFile(), getFactory()); } - private final class FileChangeListenerForVersioning extends FileChangeAdapter { - @Override - public void fileDataCreated(FileEvent fe) { - if (fe.getFile() == BaseFileObj.this) { - getProvidedExtensions().createSuccess(fe.getFile()); - } - } - - /** - * Implements FileChangeListener.fileFolderCreated(FileEvent fe) - */ - @Override - public void fileFolderCreated(FileEvent fe) { - if (fe.getFile() == BaseFileObj.this) { - getProvidedExtensions().createSuccess(fe.getFile()); - } - } - - @Override - public void fileDeleted(FileEvent fe) { - if (fe.getFile() == BaseFileObj.this) { - getProvidedExtensions().deleteSuccess(fe.getFile()); - } - } - } - private static class FileEventImpl extends FileEvent implements Enumeration { private FileEventImpl next; public boolean hasMoreElements() { Index: masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FolderObj.java --- masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FolderObj.java Base (BASE) +++ masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FolderObj.java Locally Modified (Based On LOCAL) @@ -190,7 +190,7 @@ } else { FSException.io("EXC_CannotCreateFolder", folder2Create.getName(), getPath());// NOI18N } - + getProvidedExtensions().createSuccess(retVal); return retVal; } @@ -275,7 +275,7 @@ } else { FSException.io("EXC_CannotCreateData", file2Create.getName(), getPath());// NOI18N } - + getProvidedExtensions().createSuccess(retVal); return retVal; } @@ -376,10 +376,12 @@ if (newChild.isFolder()) { if (fire) { + getProvidedExtensions().createdExternally(newChild); newChild.fireFileFolderCreatedEvent(expected); } } else { if (fire) { + getProvidedExtensions().createdExternally(newChild); newChild.fireFileDataCreatedEvent(expected); } } @@ -389,9 +391,11 @@ if (newChild.isValid()) { newChild.setValid(false); if (newChild instanceof FolderObj) { + getProvidedExtensions().deletedExternally(newChild); ((FolderObj)newChild).refreshImpl(expected, fire); } else { if (fire) { + getProvidedExtensions().deletedExternally(newChild); newChild.fireFileDeletedEvent(expected); } } Index: masterfs/src/org/netbeans/modules/masterfs/providers/InterceptionListener.java --- masterfs/src/org/netbeans/modules/masterfs/providers/InterceptionListener.java Base (BASE) +++ masterfs/src/org/netbeans/modules/masterfs/providers/InterceptionListener.java Locally Modified (Based On LOCAL) @@ -56,4 +56,7 @@ void beforeDelete(org.openide.filesystems.FileObject fo); void deleteSuccess(org.openide.filesystems.FileObject fo); void deleteFailure(org.openide.filesystems.FileObject fo); + void beforeMove(org.openide.filesystems.FileObject from, java.io.File to); + void moveSuccess(org.openide.filesystems.FileObject from, java.io.File to); + void moveFailure(org.openide.filesystems.FileObject from, java.io.File to); } Index: masterfs/src/org/netbeans/modules/masterfs/providers/ProvidedExtensions.java --- masterfs/src/org/netbeans/modules/masterfs/providers/ProvidedExtensions.java Base (BASE) +++ masterfs/src/org/netbeans/modules/masterfs/providers/ProvidedExtensions.java Locally Modified (Based On LOCAL) @@ -145,6 +145,60 @@ public void beforeDelete(FileObject fo) {} /** + * Called by MasterFileSystem after FileObject + * was created externally + * @param fo created file + * @since 2.27 + */ + public void createdExternally(FileObject fo) {} + + /** + * Called by MasterFileSystem after FileObject + * was deleted externally + * @param fo deleted file + * @since 2.27 + */ + public void deletedExternally(FileObject fo) {} + + /** + * Called by MasterFileSystem after FileObject + * was changed + * @param fo changed file + * @since 2.27 + */ + public void fileChanged(FileObject fo) {} + + /** + * Called by MasterFileSystem before FileObject + * is moved + * @param from FileObject to be moved + * @param to File target to move this file to + * @since 2.27 + */ + @Override + public void beforeMove(FileObject from, File to) {} + + /** + * Called by MasterFileSystem after FileObject + * was successfully + * @param from FileObject to be moved + * @param to File target to move this file to + * @since 2.27 + */ + @Override + public void moveSuccess(FileObject from, File to) {} + + /** + * Called by MasterFileSystem after a FileObject + * move failed + * @param from FileObject to be moved + * @param to File target to move this file to + * @since 2.27 + */ + @Override + public void moveFailure(FileObject from, File to) {} + + /** * Called by MasterFileSystem when FileObject is queried for writability with the * canWrite() method. * Index: masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/InterceptionListenerTest.java --- masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/InterceptionListenerTest.java Base (BASE) +++ masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/InterceptionListenerTest.java Locally Modified (Based On LOCAL) @@ -44,6 +44,7 @@ package org.netbeans.modules.masterfs.providers; +import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Iterator; @@ -51,6 +52,7 @@ import javax.swing.Action; import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; +import org.openide.filesystems.FileLock; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.Lookup; @@ -145,6 +147,43 @@ } } + public void testMove_BeforeSuccessFailure() throws IOException { + FileObject fromFolder = FileUtil.toFileObject(getWorkDir()).createFolder("moveFrom"); + FileObject toFolder = FileUtil.toFileObject(getWorkDir()).createFolder("moveTo"); + assertNotNull(fromFolder); + assertNotNull(toFolder); + FileObject toMove = fromFolder.createData("aa"); + assertNotNull(toMove); + iListener.clear(); + + assertNotNull(iListener); + assertEquals(0,iListener.beforeMoveCalls); + assertEquals(0,iListener.moveSuccessCalls); + assertEquals(0,iListener.moveFailureCalls); + + // move + FileLock lock = toMove.lock(); + toMove.move(lock, toFolder, toMove.getName(), toMove.getExt()); + assertFalse(toMove.isValid()); + assertEquals(1,iListener.beforeMoveCalls); + assertEquals(1,iListener.moveSuccessCalls); + + iListener.clear(); + try { + // success + assertEquals(0,iListener.moveSuccessCalls); + assertEquals(0,iListener.moveFailureCalls); + + // move to itself => failure + toMove.move(lock, toFolder, toMove.getName(), toMove.getExt()); + fail(); + } catch (IOException ex) { + // failure + assertEquals(0,iListener.moveSuccessCalls); + assertEquals(1,iListener.moveFailureCalls); + } + } + public static class AnnotationProviderImpl extends AnnotationProvider { private static int cnt; @@ -200,6 +239,9 @@ private int beforeDeleteCalls = 0; private int deleteSuccessCalls = 0; private int deleteFailureCalls = 0; + private int beforeMoveCalls = 0; + private int moveSuccessCalls = 0; + private int moveFailureCalls = 0; private final AnnotationProviderImpl provider; public InterceptionListenerImpl(AnnotationProviderImpl provider) { @@ -213,6 +255,9 @@ beforeDeleteCalls = 0; deleteSuccessCalls = 0; deleteFailureCalls = 0; + beforeMoveCalls = 0; + moveSuccessCalls = 0; + moveFailureCalls = 0; } public void beforeCreate(org.openide.filesystems.FileObject parent, java.lang.String name, boolean isFolder) { @@ -239,5 +284,17 @@ public void deleteFailure(org.openide.filesystems.FileObject fo) { deleteFailureCalls++; } + + public void beforeMove(FileObject fo, File to) { + beforeMoveCalls++; } + + public void moveSuccess(FileObject fo, File to) { + moveSuccessCalls++; } + + public void moveFailure(FileObject fo, File to) { + moveFailureCalls++; + } + } +} Index: masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/ProvidedExtensionsTest.java --- masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/ProvidedExtensionsTest.java Base (BASE) +++ masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/ProvidedExtensionsTest.java Locally Modified (Based On LOCAL) @@ -240,17 +240,65 @@ } } - public void testAfterAtomicAction() throws IOException { + public void testDuringAtomicAction() throws IOException { FileObject fo = FileUtil.toFileObject(getWorkDir()); + iListener.clear(); fo.getFileSystem().runAtomicAction(new FileSystem.AtomicAction() { public void run() throws IOException { - FileUtil.createData(new File(getWorkDir(),"a/b/c/d/e/f/g.txt")); - assertEquals(0, iListener.implsCreateSuccessCalls); + FileObject f = FileUtil.createData(new File(getWorkDir(), "a/b/c/d/e/f/g.txt")); + assertEquals(7, iListener.implsCreateSuccessCalls); + f.delete(); + assertEquals(1, iListener.implsDeleteSuccessCalls); } }); - assertEquals(7, iListener.implsCreateSuccessCalls); } + public void testCreatedExternally() throws IOException { + FileObject fo = FileUtil.toFileObject(getWorkDir()); + FileObject[] children = fo.getChildren(); // scan folder + + FileObject folder = fo.createFolder("folder"); + iListener.clear(); + assertEquals(0, iListener.implsCreatedExternallyCalls); + File f = new File(FileUtil.toFile(fo), "file"); + f.createNewFile(); + assertEquals(0, iListener.implsCreatedExternallyCalls); + fo.refresh(); + assertEquals(1, iListener.implsCreatedExternallyCalls); + } + + public void testDeletedExternally() throws IOException { + FileObject fo = FileUtil.toFileObject(getWorkDir()); + FileObject file = fo.createData("file"); + + iListener.clear(); + FileUtil.toFile(file).delete(); + assertEquals(0, iListener.implsDeletedExternallyCalls); + fo.refresh(); + assertEquals(1, iListener.implsDeletedExternallyCalls); + } + + public void testFileChangedExternally() throws IOException { + FileObject fo = FileUtil.toFileObject(getWorkDir()); + FileObject file = fo.createData("file"); + FileUtil.toFile(file).setLastModified(System.currentTimeMillis() - 10000); + fo.refresh(); + + iListener.clear(); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(FileUtil.toFile(file)); + fos.write("data".getBytes()); + fos.flush(); + } finally { + if(fos != null) fos.close(); + } + assertEquals(0, iListener.implsFileChangedCalls); + fo.refresh(); + file.refresh(); + assertEquals(1, iListener.implsFileChangedCalls); + } + public void testImplsRename2() throws IOException { final List events = new ArrayList(); FileObject fo = FileUtil.toFileObject(getWorkDir()); @@ -464,6 +512,10 @@ private int renameImplCalls; private int implsBeforeChangeCalls; private int implsCreateSuccessCalls; + private int implsDeleteSuccessCalls; + private int implsCreatedExternallyCalls; + private int implsDeletedExternallyCalls; + private int implsFileChangedCalls; private int implsFileLockCalls; private int implsFileUnlockCalls; private int implsCanWriteCalls; @@ -501,6 +553,10 @@ renameImplCalls = 0; implsBeforeChangeCalls = 0; implsCreateSuccessCalls = 0; + implsDeleteSuccessCalls = 0; + implsCreatedExternallyCalls = 0; + implsDeletedExternallyCalls = 0; + implsFileChangedCalls = 0; implsFileLockCalls = 0; implsCanWriteCalls = 0; } @@ -527,11 +583,27 @@ implsCreateSuccessCalls++; } + public void deleteSuccess(FileObject fo) { + implsDeleteSuccessCalls++; + } + public void beforeChange(FileObject f) { assertNotNull(FileUtil.toFile(f)); implsBeforeChangeCalls++; } + public void createdExternally(FileObject fo) { + implsCreatedExternallyCalls++; + } + + public void deletedExternally(FileObject fo) { + implsDeletedExternallyCalls++; + } + + public void fileChanged(FileObject fo) { + implsFileChangedCalls++; + } + public static void nextRefreshCall(File forDir, long retValue, File... toAdd) { refreshCallForDir = forDir; refreshCallRetValue = retValue;