changeset: 145752:edcdb6e56204 tag: vcs-indexing-bridge tag: tip tag: qtip tag: qbase user: Vita Stejskal date: Thu Sep 17 19:03:58 2009 +0200 summary: #167098: Ignore VCS generated file events during large VCS operations diff -r bb1ea98ebe9a -r edcdb6e56204 nbbuild/cluster.properties --- a/nbbuild/cluster.properties Thu Sep 17 13:16:23 2009 +0200 +++ b/nbbuild/cluster.properties Thu Sep 17 19:03:58 2009 +0200 @@ -407,6 +407,7 @@ versioning,\ versioning.system.cvss,\ versioning.util,\ + versioning.indexingbridge, \ web.client.tools.api,\ web.flyingsaucer,\ xml,\ diff -r bb1ea98ebe9a -r edcdb6e56204 parsing.api/nbproject/project.properties --- a/parsing.api/nbproject/project.properties Thu Sep 17 13:16:23 2009 +0200 +++ b/parsing.api/nbproject/project.properties Thu Sep 17 19:03:58 2009 +0200 @@ -2,4 +2,4 @@ javac.source=1.5 javadoc.apichanges=${basedir}/apichanges.xml javadoc.arch=${basedir}/arch.xml -spec.version.base=1.23.0 +spec.version.base=1.24.0 diff -r bb1ea98ebe9a -r edcdb6e56204 parsing.api/src/org/netbeans/modules/parsing/api/indexing/IndexingManager.java --- a/parsing.api/src/org/netbeans/modules/parsing/api/indexing/IndexingManager.java Thu Sep 17 13:16:23 2009 +0200 +++ b/parsing.api/src/org/netbeans/modules/parsing/api/indexing/IndexingManager.java Thu Sep 17 19:03:58 2009 +0200 @@ -39,12 +39,15 @@ package org.netbeans.modules.parsing.api.indexing; +import java.io.File; import java.net.URL; import java.util.Collection; +import java.util.concurrent.Callable; import javax.swing.SwingUtilities; import org.netbeans.modules.parsing.impl.Utilities; import org.netbeans.modules.parsing.impl.event.EventSupport; import org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater; +import org.netbeans.modules.parsing.impl.indexing.friendapi.IndexingController; import org.openide.filesystems.FileObject; /** @@ -232,8 +235,40 @@ * * @since 1.23 */ - public void refreshAllIndices(boolean fullRescan, boolean wait, FileObject... folders) { - RepositoryUpdater.getDefault().refreshAll(fullRescan, wait, false, folders); + public void refreshAllIndices(boolean fullRescan, boolean wait, FileObject... fileObjects) { + RepositoryUpdater.getDefault().refreshAll(fullRescan, wait, false, (Object []) fileObjects); + } + + /** + * + * @param fullRescan + * @param wait + * @param files + * + * @since 1.24 + */ + public void refreshAllIndices(boolean fullRescan, boolean wait, File... files) { + RepositoryUpdater.getDefault().refreshAll(fullRescan, wait, false, (Object []) files); + } + + /** + * Runs the operation in protected mode. All events that would normally + * trigger rescanning are rememberd and processed after the operation finishes. + * + * @param operation The operation to run without rescanning while the operation + * is running. + * @return Whatever value the operation returns. + * + * @throws Exception Any exception thrown from the operation is rethrown. + * @since 1.24 + */ + public T runProtected(Callable operation) throws Exception { + IndexingController.getDefault().enterProtectedMode(); + try { + return operation.call(); + } finally { + IndexingController.getDefault().exitProtectedMode(null); + } } // ----------------------------------------------------------------------- diff -r bb1ea98ebe9a -r edcdb6e56204 parsing.api/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java --- a/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java Thu Sep 17 13:16:23 2009 +0200 +++ b/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java Thu Sep 17 19:03:58 2009 +0200 @@ -311,7 +311,7 @@ } } - public void refreshAll(boolean fullRescan, boolean wait, boolean logStatistics, FileObject... folders) { + public void refreshAll(boolean fullRescan, boolean wait, boolean logStatistics, Object... filesOrFileObjects) { FSRefreshInterceptor fsRefreshInterceptor = null; for(IndexingActivityInterceptor iai : indexingActivityInterceptors.allInstances()) { if (iai instanceof FSRefreshInterceptor) { @@ -321,7 +321,7 @@ } scheduleWork( - new RefreshWork(scannedRoots2Dependencies, scannedBinaries, sourcesForBinaryRoots, fullRescan, logStatistics, Arrays.asList(folders), fsRefreshInterceptor), + new RefreshWork(scannedRoots2Dependencies, scannedBinaries, sourcesForBinaryRoots, fullRescan, logStatistics, Arrays.asList(filesOrFileObjects), fsRefreshInterceptor), wait); } @@ -2181,8 +2181,8 @@ private final Map> scannedRoots2Dependencies; private final Set scannedBinaries; private final Set sourcesForBinaryRoots; - private final boolean fullRescan; - private final List suspectFolders; + private boolean fullRescan; + private final Set suspectFilesOdFileObjects; private final FSRefreshInterceptor interceptor; private DependenciesContext depCtx; @@ -2193,7 +2193,7 @@ Set sourcesForBinaryRoots, boolean fullRescan, boolean logStatistics, - List suspectFolders, + Collection suspectFilesOdFileObjects, FSRefreshInterceptor interceptor) { super(false, false, true, fullRescan, logStatistics); @@ -2201,14 +2201,14 @@ Parameters.notNull("scannedRoots2Depencencies", scannedRoots2Depencencies); //NOI18N Parameters.notNull("scannedBinaries", scannedBinaries); //NOI18N Parameters.notNull("sourcesForBinaryRoots", sourcesForBinaryRoots); //NOI18N - Parameters.notNull("suspectFolders", suspectFolders); //NOI18N + Parameters.notNull("suspectFilesOdFileObjects", suspectFilesOdFileObjects); //NOI18N Parameters.notNull("interceptor", interceptor); //NOI18N this.scannedRoots2Dependencies = scannedRoots2Depencencies; this.scannedBinaries = scannedBinaries; this.sourcesForBinaryRoots = sourcesForBinaryRoots; this.fullRescan = fullRescan; - this.suspectFolders = suspectFolders; + this.suspectFilesOdFileObjects = new HashSet(suspectFilesOdFileObjects); this.interceptor = interceptor; } @@ -2225,44 +2225,115 @@ } Collections.reverse(depCtx.newRootsToScan); - if (suspectFolders != null && suspectFolders.size() > 0) { - // filter binary roots - for(Iterator it = depCtx.newBinariesToScan.iterator(); it.hasNext(); ) { - URL root = it.next(); - boolean suspect = false; - File rootFile = FileUtil.archiveOrDirForURL(root); - if (rootFile != null) { - FileObject rootFo = FileUtil.toFileObject(rootFile); + if (suspectFilesOdFileObjects != null && suspectFilesOdFileObjects.size() > 0) { + Set suspects = new HashSet(); + for(Object fileOrFileObject : suspectFilesOdFileObjects) { + if (fileOrFileObject instanceof File) { + FileObject f = FileUtil.toFileObject((File) fileOrFileObject); + if (f != null) { + suspects.add(f); + } + } else if (fileOrFileObject instanceof FileObject) { + suspects.add((FileObject) fileOrFileObject); + } else { + LOGGER.fine("Not File or FileObject, ignoring: " + fileOrFileObject); //NOI18N + } + } + Set processedSuspects = new HashSet(); + + { // + Set> binariesNotToScan = new HashSet>(); + for(URL root : depCtx.newBinariesToScan) { + Pair pair = null; + boolean suspect = false; + File rootFile = FileUtil.archiveOrDirForURL(root); + if (rootFile != null) { + FileObject rootFo = FileUtil.toFileObject(rootFile); + if (rootFo != null) { + pair = Pair.of(root, rootFo); + for(FileObject f : suspects) { + if (f.isData()) { + continue; + } + if (FileUtil.isParentOf(f, rootFo)) { + processedSuspects.add(f); + suspect = true; + break; + } + } + } + } + if (!suspect) { + binariesNotToScan.add(pair == null ? Pair.of(root, (FileObject)null) : pair); + } + } + + for(Iterator> it = binariesNotToScan.iterator() ; it.hasNext(); ) { + Pair pair = it.next(); + FileObject rootFo = pair.second; if (rootFo != null) { - for(FileObject folder : suspectFolders) { - if (FileUtil.isParentOf(folder, rootFo)) { + for(FileObject f : suspects) { + if (processedSuspects.contains(f)) { + continue; + } + if (FileUtil.isParentOf(rootFo, f)) { + it.remove(); + break; + } + } + } + } + + for(Pair pair : binariesNotToScan) { + depCtx.newBinariesToScan.remove(pair.first); + } + // + } + + { // + Set> sourcesNotToScan = new HashSet>(); + for(URL root : depCtx.newRootsToScan) { + Pair pair = null; + boolean suspect = false; + FileObject rootFo = URLCache.getInstance().findFileObject(root); + if (rootFo != null) { + pair = Pair.of(root, rootFo); + for(FileObject f : suspects) { + if (f.isData()) { + continue; + } + if (FileUtil.isParentOf(f, rootFo)) { + processedSuspects.add(f); suspect = true; break; } } } + if (!suspect) { + sourcesNotToScan.add(pair == null ? Pair.of(root, (FileObject)null) : pair); + } } - if (!suspect) { - it.remove(); - } - } - // filter source roots - for(Iterator it = depCtx.newRootsToScan.iterator(); it.hasNext(); ) { - URL root = it.next(); - boolean suspect = false; - FileObject rootFo = URLCache.getInstance().findFileObject(root); - if (rootFo != null) { - for(FileObject folder : suspectFolders) { - if (FileUtil.isParentOf(folder, rootFo)) { - suspect = true; - break; + for(Iterator> it = sourcesNotToScan.iterator() ; it.hasNext(); ) { + Pair pair = it.next(); + FileObject rootFo = pair.second; + if (rootFo != null) { + for(FileObject f : suspects) { + if (processedSuspects.contains(f)) { + continue; + } + if (FileUtil.isParentOf(rootFo, f)) { + it.remove(); + break; + } } } } - if (!suspect) { - it.remove(); - } + + for(Pair pair : sourcesNotToScan) { + depCtx.newRootsToScan.remove(pair.first); + } + // } } @@ -2314,6 +2385,25 @@ return finished; } + public @Override boolean absorb(Work newWork) { + if (newWork instanceof RefreshWork) { + if (((RefreshWork) newWork).fullRescan) { + fullRescan = true; + } + suspectFilesOdFileObjects.addAll(((RefreshWork) newWork).suspectFilesOdFileObjects); + return true; + } else if (newWork instanceof FileListWork) { + if (fullRescan) { + // complicated, should check that newWork.root is reachable from suspectFilesOdFileObjects, + // but don't want to convert java.io.File to FileObjects at this moment as it could + // fire events from filesystems + } else if (!((FileListWork) newWork).forceRefresh) { + suspectFilesOdFileObjects.add(URLCache.getInstance().findFileObject(((FileListWork) newWork).root)); + return true; + } + } + return false; + } } // End of RefreshWork class private static class RootsWork extends AbstractRootsWork { diff -r bb1ea98ebe9a -r edcdb6e56204 subversion/nbproject/project.xml --- a/subversion/nbproject/project.xml Thu Sep 17 13:16:23 2009 +0200 +++ b/subversion/nbproject/project.xml Thu Sep 17 19:03:58 2009 +0200 @@ -160,7 +160,7 @@ - 1.4 + 1.6 diff -r bb1ea98ebe9a -r edcdb6e56204 subversion/src/org/netbeans/modules/subversion/client/SvnClientInvocationHandler.java --- a/subversion/src/org/netbeans/modules/subversion/client/SvnClientInvocationHandler.java Thu Sep 17 13:16:23 2009 +0200 +++ b/subversion/src/org/netbeans/modules/subversion/client/SvnClientInvocationHandler.java Thu Sep 17 19:03:58 2009 +0200 @@ -46,11 +46,14 @@ import java.lang.reflect.Method; import java.net.MalformedURLException; import java.security.InvalidKeyException; +import java.util.concurrent.Callable; import java.util.logging.Level; +import java.util.logging.Logger; import javax.net.ssl.SSLKeyException; import org.netbeans.modules.subversion.Subversion; import org.netbeans.modules.subversion.config.SvnConfigFiles; import org.netbeans.modules.subversion.util.SvnUtils; +import org.netbeans.modules.versioning.util.IndexingBridge; import org.netbeans.modules.versioning.util.Utils; import org.openide.util.Cancellable; import org.tigris.subversion.svnclientadapter.ISVNClientAdapter; @@ -69,7 +72,7 @@ protected static final String GET_INFO_FROM_WORKING_COPY = "getInfoFromWorkingCopy"; // NOI18N protected static final String CANCEL_OPERATION = "cancel"; //NOI18N - private static Object semaphor = new Object(); + private static final Object semaphor = new Object(); private final ISVNClientAdapter adapter; private final SvnClientDescriptor desc; @@ -116,18 +119,33 @@ /** * @see InvocationHandler#invoke(Object proxy, Method method, Object[] args) */ - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { + // XXX: list here all operations that can potentially modify files on the disk + final boolean fsReadOnlyAction = + !method.getName().equals("update") && //NOI18N + !method.getName().equals("revert") && //NOI18N + !method.getName().equals("merge"); //NOI18N + + try { + Logger.getLogger(SvnClientInvocationHandler.class.getName()).fine("~~~ SVN: invoking '" + method.getName() + "'"); + //new Throwable("~~~ SVN: invoking '" + method.getName() + "'").printStackTrace(); - try { - Object ret = null; - if(parallelizable(method, args)) { - ret = invokeMethod(method, args); + Callable c = new Callable() { + public Object call() throws Exception { + if(parallelizable(method, args)) { + return invokeMethod(method, args); + } else { + synchronized (semaphor) { + return invokeMethod(method, args); + } + } + } + }; + if (fsReadOnlyAction) { + return c.call(); } else { - synchronized (semaphor) { - ret = invokeMethod(method, args); - } - } - return ret; + return IndexingBridge.getInstance().runWithoutIndexing(c); + } } catch (Exception e) { try { if(handleException((SvnClient) proxy, e) ) { @@ -183,7 +201,7 @@ } finally { // whatever command was invoked, whatever the result is - // call refresh for all files notified by the client adapter - Subversion.getInstance().getRefreshHandler().refresh(); + Subversion.getInstance().getRefreshHandler().refresh(fsReadOnlyAction); } } diff -r bb1ea98ebe9a -r edcdb6e56204 subversion/src/org/netbeans/modules/subversion/client/SvnClientRefreshHandler.java --- a/subversion/src/org/netbeans/modules/subversion/client/SvnClientRefreshHandler.java Thu Sep 17 13:16:23 2009 +0200 +++ b/subversion/src/org/netbeans/modules/subversion/client/SvnClientRefreshHandler.java Thu Sep 17 19:03:58 2009 +0200 @@ -44,6 +44,7 @@ import java.util.Set; import java.util.logging.Level; import org.netbeans.modules.subversion.Subversion; +import org.netbeans.modules.versioning.util.IndexingBridge; import org.openide.filesystems.FileUtil; import org.openide.util.RequestProcessor; import org.tigris.subversion.svnclientadapter.ISVNNotifyListener; @@ -90,31 +91,31 @@ * Refresh the nb filesystem and the cache for all given files * @param files files to be refreshed */ - public void refreshImediately(File... files) { + public void refreshImediately(boolean simpleRefresh, File... files) { for (int i = 0; i < files.length; i++) { files[i] = FileUtil.normalizeFile(files[i]); // I saw "./" } - refresh(files); + refresh(simpleRefresh, files); } /** * Refreshes all yet notified files * @see {@link #onNotify(java.io.File, org.tigris.subversion.svnclientadapter.SVNNodeKind)} */ - public void refresh() { + public void refresh(boolean simpleRefresh) { File[] fileArray; synchronized(filesToRefresh) { fileArray = filesToRefresh.toArray(new File[filesToRefresh.size()]); filesToRefresh.clear(); } - refresh(fileArray); + refresh(simpleRefresh, fileArray); } /** * Refresh the nb filesystem and the cache for all given files * @param files files to be refreshed */ - private void refresh(File... files) { + private void refresh(boolean simpleRefresh, File... files) { if(Subversion.LOG.isLoggable(Level.FINE)) { for (File file : files) { Subversion.LOG.fine("refreshing: [" + file + "]"); // NOI18N @@ -122,7 +123,11 @@ } // refresh the filesystems first as the following cache refesh might fire events // which are intercepted by the nb filesystem - it has to be aware about possible changes made - refreshFS(files); + if (simpleRefresh) { + refreshFS(files); + } else { + IndexingBridge.getInstance().refreshFiles(files); + } // async cache refesh - notifications from the svnclientadapter may be caused // by a synchronously handled FS event. If we want to (have to) prevent // reentrant calls on the FS api diff -r bb1ea98ebe9a -r edcdb6e56204 subversion/src/org/netbeans/modules/subversion/ui/copy/SwitchToAction.java --- a/subversion/src/org/netbeans/modules/subversion/ui/copy/SwitchToAction.java Thu Sep 17 13:16:23 2009 +0200 +++ b/subversion/src/org/netbeans/modules/subversion/ui/copy/SwitchToAction.java Thu Sep 17 19:03:58 2009 +0200 @@ -44,6 +44,7 @@ import java.awt.EventQueue; import java.io.File; import java.util.List; +import java.util.concurrent.Callable; import org.netbeans.modules.subversion.FileInformation; import org.netbeans.modules.subversion.RepositoryFile; import org.netbeans.modules.subversion.Subversion; @@ -53,6 +54,7 @@ import org.netbeans.modules.subversion.ui.actions.ContextAction; import org.netbeans.modules.subversion.util.Context; import org.netbeans.modules.subversion.util.SvnUtils; +import org.netbeans.modules.versioning.util.IndexingBridge; import org.netbeans.modules.versioning.util.Utils; import org.openide.nodes.Node; import org.openide.util.NbBundle; @@ -191,39 +193,49 @@ return ret; } - static void performSwitch(RepositoryFile toRepositoryFile, File root, SvnProgressSupport support) { - File[][] split = Utils.splitFlatOthers(new File[] {root} ); - boolean recursive; - // there can be only 1 root file - if(split[0].length > 0) { - recursive = false; - } else { - recursive = true; - } + static void performSwitch(final RepositoryFile toRepositoryFile, final File root, final SvnProgressSupport support) { + try { + IndexingBridge.getInstance().runWithoutIndexing(new Callable() { + public Void call() { + File[][] split = Utils.splitFlatOthers(new File[] {root} ); + boolean recursive; + // there can be only 1 root file + if(split[0].length > 0) { + recursive = false; + } else { + recursive = true; + } - try { - SvnClient client; - try { - client = Subversion.getInstance().getClient(toRepositoryFile.getRepositoryUrl()); - } catch (SVNClientException ex) { - SvnClientExceptionHandler.notifyException(ex, true, true); - return; - } - // ... and switch - client.switchToUrl(root, toRepositoryFile.getFileUrl(), toRepositoryFile.getRevision(), recursive); - // the client doesn't notify as there is no output rom the cli. Lets emulate onNotify as from the client - List switchedFiles = SvnUtils.listRecursively(root); - File[] fileArray = switchedFiles.toArray(new File[switchedFiles.size()]); - Subversion.getInstance().getRefreshHandler().refreshImediately(fileArray); - // the cache fires status change events to trigger the annotation refresh - // unfortunatelly - we have to call the refresh explicitly for each file also - // from this place as the branch label was changed evern if the files status didn't - Subversion.getInstance().getStatusCache().getLabelsCache().flushFileLabels(fileArray); - Subversion.getInstance().refreshAnnotations(fileArray); - // refresh the inline diff - Subversion.getInstance().versionedFilesChanged(); - } catch (SVNClientException ex) { - support.annotate(ex); + try { + SvnClient client; + try { + client = Subversion.getInstance().getClient(toRepositoryFile.getRepositoryUrl()); + } catch (SVNClientException ex) { + SvnClientExceptionHandler.notifyException(ex, true, true); + return null; + } + // ... and switch + client.switchToUrl(root, toRepositoryFile.getFileUrl(), toRepositoryFile.getRevision(), recursive); + // the client doesn't notify as there is no output rom the cli. Lets emulate onNotify as from the client + List switchedFiles = SvnUtils.listRecursively(root); + File[] fileArray = switchedFiles.toArray(new File[switchedFiles.size()]); + Subversion.getInstance().getRefreshHandler().refreshImediately(false, fileArray); + // the cache fires status change events to trigger the annotation refresh + // unfortunatelly - we have to call the refresh explicitly for each file also + // from this place as the branch label was changed evern if the files status didn't + Subversion.getInstance().getStatusCache().getLabelsCache().flushFileLabels(fileArray); + Subversion.getInstance().refreshAnnotations(fileArray); + // refresh the inline diff + Subversion.getInstance().versionedFilesChanged(); + } catch (SVNClientException ex) { + support.annotate(ex); + } + + return null; + } + }); + } catch (Exception e) { + e.printStackTrace(); } } } diff -r bb1ea98ebe9a -r edcdb6e56204 versioning.indexingbridge/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/versioning.indexingbridge/build.xml Thu Sep 17 19:03:58 2009 +0200 @@ -0,0 +1,5 @@ + + + Builds, tests, and runs the project org.netbeans.modules.versioning.indexingbridge + + diff -r bb1ea98ebe9a -r edcdb6e56204 versioning.indexingbridge/manifest.mf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/versioning.indexingbridge/manifest.mf Thu Sep 17 19:03:58 2009 +0200 @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: org.netbeans.modules.versioning.indexingbridge/0 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/versioning/indexingbridge/Bundle.properties +OpenIDE-Module-Provides: org.netbeans.modules.versioning.indexingbridge +OpenIDE-Module-Specification-Version: 1.0 + diff -r bb1ea98ebe9a -r edcdb6e56204 versioning.indexingbridge/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/versioning.indexingbridge/nbproject/project.properties Thu Sep 17 19:03:58 2009 +0200 @@ -0,0 +1,3 @@ +is.eager=true +javac.source=1.5 +javac.compilerargs=-Xlint -Xlint:-serial diff -r bb1ea98ebe9a -r edcdb6e56204 versioning.indexingbridge/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/versioning.indexingbridge/nbproject/project.xml Thu Sep 17 19:03:58 2009 +0200 @@ -0,0 +1,44 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.versioning.indexingbridge + + + org.netbeans.modules.parsing.api + + + + 1.23 + + + + org.netbeans.modules.versioning.util + + + + 1.6 + + + + org.openide.filesystems + + + + 7.27 + + + + org.openide.util + + + + 7.27 + + + + + + + diff -r bb1ea98ebe9a -r edcdb6e56204 versioning.indexingbridge/src/org/netbeans/modules/versioning/indexingbridge/Bridge.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/versioning.indexingbridge/src/org/netbeans/modules/versioning/indexingbridge/Bridge.java Thu Sep 17 19:03:58 2009 +0200 @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.versioning.indexingbridge; + +import java.io.File; +import java.util.concurrent.Callable; +import org.netbeans.modules.parsing.api.indexing.IndexingManager; +import org.netbeans.modules.versioning.util.IndexingBridge.IndexingBridgeProvider; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author vita + */ +@ServiceProvider(service=IndexingBridgeProvider.class) +public final class Bridge implements IndexingBridgeProvider { + + public T runWithoutIndexing(Callable operation) throws Exception { + return IndexingManager.getDefault().runProtected(operation); + } + + public void refreshFiles(File... files) { + IndexingManager.getDefault().refreshAllIndices(false, false, files); + } + +} diff -r bb1ea98ebe9a -r edcdb6e56204 versioning.indexingbridge/src/org/netbeans/modules/versioning/indexingbridge/Bundle.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/versioning.indexingbridge/src/org/netbeans/modules/versioning/indexingbridge/Bundle.properties Thu Sep 17 19:03:58 2009 +0200 @@ -0,0 +1,1 @@ +OpenIDE-Module-Name=Versioning-Indexing Bridge diff -r bb1ea98ebe9a -r edcdb6e56204 versioning.util/manifest.mf --- a/versioning.util/manifest.mf Thu Sep 17 13:16:23 2009 +0200 +++ b/versioning.util/manifest.mf Thu Sep 17 19:03:58 2009 +0200 @@ -2,4 +2,4 @@ OpenIDE-Module: org.netbeans.modules.versioning.util OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/versioning/util/Bundle.properties OpenIDE-Module-Layer: org/netbeans/modules/versioning/util/resources/layer.xml - +OpenIDE-Module-Recommends: org.netbeans.modules.versioning.indexingbridge diff -r bb1ea98ebe9a -r edcdb6e56204 versioning.util/nbproject/project.properties --- a/versioning.util/nbproject/project.properties Thu Sep 17 13:16:23 2009 +0200 +++ b/versioning.util/nbproject/project.properties Thu Sep 17 19:03:58 2009 +0200 @@ -41,7 +41,7 @@ javac.source=1.5 javadoc.name=Versioning Support Utilities -spec.version.base=1.5.0 +spec.version.base=1.6.0 is.autoload=true # Fatal error: Fatal error: class javax.net.SocketFactory not found diff -r bb1ea98ebe9a -r edcdb6e56204 versioning.util/nbproject/project.xml --- a/versioning.util/nbproject/project.xml Thu Sep 17 13:16:23 2009 +0200 +++ b/versioning.util/nbproject/project.xml Thu Sep 17 19:03:58 2009 +0200 @@ -209,17 +209,18 @@ + org.netbeans.modules.bugtracking.bridge org.netbeans.modules.clearcase + org.netbeans.modules.localhistory org.netbeans.modules.mercurial org.netbeans.modules.perforce + org.netbeans.modules.subversion + org.netbeans.modules.versioning.indexingbridge org.netbeans.modules.versioning.system.cvss - org.netbeans.modules.subversion - org.netbeans.modules.localhistory - org.netbeans.modules.bugtracking.bridge + org.netbeans.modules.proxy + org.netbeans.modules.turbo org.netbeans.modules.versioning.annotate org.netbeans.modules.versioning.util - org.netbeans.modules.proxy - org.netbeans.modules.turbo diff -r bb1ea98ebe9a -r edcdb6e56204 versioning.util/src/org/netbeans/modules/versioning/util/IndexingBridge.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/versioning.util/src/org/netbeans/modules/versioning/util/IndexingBridge.java Thu Sep 17 19:03:58 2009 +0200 @@ -0,0 +1,179 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.versioning.util; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.logging.Logger; +import org.openide.filesystems.FileUtil; +import org.openide.util.Lookup; +import org.openide.util.RequestProcessor; +import org.openide.util.lookup.ServiceProvider; + +/** + * This class provides means for tight integration between VCS modules and the indexing + * infrastructure. + * + * @author Vita Stejskal + * @since 1.6 + */ +public final class IndexingBridge { + + // ----------------------------------------------------------------------- + // Public implementation + // ----------------------------------------------------------------------- + + /** + * Gets the signleton instance of IndexingBridge. + * + * @return The IndexingBridge instance. + */ + public static synchronized IndexingBridge getInstance() { + if (instance == null) { + instance = new IndexingBridge(); + } + return instance; + } + + /** + * Runs the operation without interfering with indexing. The indexing + * is blocked while the operation is runing and all events that would normally trigger + * reindexing will be processed after the operation is finished. + * + * @param operation The operation to run. + * @return Whatever value is returned from the operation. + * + * @throws Exception Any exception thrown by the operation is going to be rethrown from + * this method. + */ + public T runWithoutIndexing(Callable operation) throws Exception { + IndexingBridgeProvider ibp = Lookup.getDefault().lookup(IndexingBridgeProvider.class); + if (ibp != null) { + return ibp.runWithoutIndexing(operation); + } else { + return operation.call(); + } + } + + public void refreshFiles(File... files) { + final Set parents = new HashSet(); + for (File f : files) { + File parent = f.getParentFile(); + if (parent != null) { + parents.add(parent); + LOG.fine("scheduling for fs refresh: [" + parent + "]"); // NOI18N + } + } + + if (parents.size() > 0) { + IndexingBridgeProvider ibp = Lookup.getDefault().lookup(IndexingBridgeProvider.class); + if (ibp != null) { + ibp.refreshFiles(parents.toArray(new File[parents.size()])); + } else { + // let's give the filesystem some time to wake up and to realize that the file has really changed + RequestProcessor.getDefault().post(new Runnable() { + public void run() { + FileUtil.refreshFor(parents.toArray(new File[parents.size()])); + } + }, 100); // XXX: getDelay() + } + } + } + + /** + * This interface is supposed to be implemented by the actual bridge module + * that connect the versioning and indexing infrastructure. Ordinary VCS support + * modules do not need to implement this interface. + *

Implementations of this interface ought to be registered via {@link ServiceProvider} annotation + * in the default Lookup. + */ + public static interface IndexingBridgeProvider { + /** + * This method is te actual integration point between versioning and indexing. + * A typical implementation should do something like this: + *

    + *
  • call the indexing infrastructure and turn off the immediate processing of events
  • + *
  • call operation.call()
  • + *
  • call the indexing infrastructure to process all the events gatherd while
  • + * the operation was running and turn on the immediate processing of events + *
+ * + * @param operation The operation to run. + * @return Whatever value is returned from the operation. + * + * @throws Exception Any exception thrown by the operation is going to rethrown from + * this method. + */ + T runWithoutIndexing(Callable operation) throws Exception; + + void refreshFiles(File... files); + } + + // ----------------------------------------------------------------------- + // private implementation + // ----------------------------------------------------------------------- + + private static final Logger LOG = Logger.getLogger(IndexingBridge.class.getName()); + private static IndexingBridge instance = null; +// XXX: is this neccessary? +// private static final int DEFAULT_DELAY = 100; +// private int delayBeforeRefresh = -1; + + private IndexingBridge() { + // no-op + } + +// XXX: is this neccessary? +// private int getDelay() { +// if (delayBeforeRefresh == -1) { +// String delayProp = System.getProperty("subversion.SvnClientRefreshHandler.delay", Integer.toString(DEFAULT_DELAY)); //NOI18N +// int delay = DEFAULT_DELAY; +// try { +// delay = Integer.parseInt(delayProp); +// } catch (NumberFormatException e) { +// LOG.log(Level.FINE, null, e); +// } +// delayBeforeRefresh = delay; +// } +// return delayBeforeRefresh; +// } +}