Copyright (c) 2004, 2015 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM - Initial API and implementation James Blackburn (Broadcom Corp.) - ongoing development Lars Vogel - Bug 473427, 483862
/******************************************************************************* * Copyright (c) 2004, 2015 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM - Initial API and implementation * James Blackburn (Broadcom Corp.) - ongoing development * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427, 483862 *******************************************************************************/
package org.eclipse.core.internal.refresh; import java.util.*; import org.eclipse.core.internal.localstore.PrefixPool; import org.eclipse.core.internal.utils.Messages; import org.eclipse.core.internal.utils.Policy; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*;
The RefreshJob class maintains a list of resources that need to be refreshed, and periodically schedules itself to perform the refreshes in the background.
Since:3.0
/** * The <code>RefreshJob</code> class maintains a list of resources that * need to be refreshed, and periodically schedules itself to perform the * refreshes in the background. * * @since 3.0 */
public class RefreshJob extends WorkspaceJob { private static final long UPDATE_DELAY = 200;
List of refresh requests. Requests are processed in order from the end of the list. Requests can be added to either the beginning or the end of the list depending on whether they are explicit user requests or background refresh requests.
/** * List of refresh requests. Requests are processed in order from * the end of the list. Requests can be added to either the beginning * or the end of the list depending on whether they are explicit user * requests or background refresh requests. */
private final List<IResource> fRequests;
The history of path prefixes visited during this refresh job invocation. This is used to prevent infinite refresh loops caused by symbolic links in the file system.
/** * The history of path prefixes visited during this refresh job invocation. * This is used to prevent infinite refresh loops caused by symbolic links in the file system. */
private PrefixPool pathPrefixHistory, rootPathHistory; public RefreshJob() { super(Messages.refresh_jobName); fRequests = new ArrayList<>(1); }
Adds the given resource to the set of resources that need refreshing. Synchronized in order to protect the collection during add.
Params:
  • resource –
/** * Adds the given resource to the set of resources that need refreshing. * Synchronized in order to protect the collection during add. * @param resource */
private synchronized void addRequest(IResource resource) { IPath toAdd = resource.getFullPath(); for (Iterator<IResource> it = fRequests.iterator(); it.hasNext();) { IPath request = it.next().getFullPath(); //discard any existing requests the same or below the resource to be added if (toAdd.isPrefixOf(request)) it.remove(); //nothing to do if the resource to be added is a child of an existing request else if (request.isPrefixOf(toAdd)) return; } //finally add the new request to the front of the queue fRequests.add(resource); } private synchronized void addRequests(List<IResource> list) { //add requests to the end of the queue fRequests.addAll(0, list); } @Override public boolean belongsTo(Object family) { return family == ResourcesPlugin.FAMILY_AUTO_REFRESH; }
This method adds all members at the specified depth from the resource to the provided list.
/** * This method adds all members at the specified depth from the resource * to the provided list. */
private List<IResource> collectChildrenToDepth(IResource resource, ArrayList<IResource> children, int depth) { if (resource.getType() == IResource.FILE) return children; IResource[] members; try { members = ((IContainer) resource).members(); } catch (CoreException e) { //resource is not accessible - just return what we have return children; } for (IResource member : members) { if (member.getType() == IResource.FILE) continue; if (depth <= 1) children.add(member); else collectChildrenToDepth(member, children, depth - 1); } return children; }
Returns the path prefixes visited by this job so far.
/** * Returns the path prefixes visited by this job so far. */
public PrefixPool getPathPrefixHistory() { if (pathPrefixHistory == null) pathPrefixHistory = new PrefixPool(20); return pathPrefixHistory; }
Returns the root paths visited by this job so far.
/** * Returns the root paths visited by this job so far. */
public PrefixPool getRootPathHistory() { if (rootPathHistory == null) rootPathHistory = new PrefixPool(20); return rootPathHistory; }
Returns the next item to refresh, or null if there are no requests
/** * Returns the next item to refresh, or <code>null</code> if there are no requests */
private synchronized IResource nextRequest() { // synchronized: in order to atomically obtain and clear requests int len = fRequests.size(); if (len == 0) return null; return fRequests.remove(len - 1); }
See Also:
  • refresh.refresh
/** * @see org.eclipse.core.resources.refresh.IRefreshResult#refresh */
public void refresh(IResource resource) { if (resource == null) return; addRequest(resource); schedule(UPDATE_DELAY); } @Override public IStatus runInWorkspace(IProgressMonitor monitor) { long start = System.currentTimeMillis(); String msg = Messages.refresh_refreshErr; MultiStatus errors = new MultiStatus(ResourcesPlugin.PI_RESOURCES, 1, msg, null); long longestRefresh = 0; SubMonitor subMonitor = SubMonitor.convert(monitor); try { if (Policy.DEBUG_AUTO_REFRESH) Policy.debug(RefreshManager.DEBUG_PREFIX + " starting refresh job"); //$NON-NLS-1$ int refreshCount = 0; int depth = 2; IResource toRefresh; while ((toRefresh = nextRequest()) != null) { try { subMonitor.setWorkRemaining(Math.max(fRequests.size(), 100)); refreshCount++; long refreshTime = -System.currentTimeMillis(); toRefresh.refreshLocal(1000 + depth, subMonitor.split(1)); refreshTime += System.currentTimeMillis(); if (refreshTime > longestRefresh) longestRefresh = refreshTime; //show occasional progress if (refreshCount % 1000 == 0) { //be polite to other threads (no effect on some platforms) Thread.yield(); //throttle depth if it takes too long if (longestRefresh > 2000 && depth > 1) { depth = 1; } if (longestRefresh < 1000) { depth *= 2; } longestRefresh = 0; } addRequests(collectChildrenToDepth(toRefresh, new ArrayList<>(), depth)); } catch (CoreException e) { errors.merge(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, 1, errors.getMessage(), e)); } } } finally { pathPrefixHistory = null; rootPathHistory = null; if (Policy.DEBUG_AUTO_REFRESH) Policy.debug(RefreshManager.DEBUG_PREFIX + " finished refresh job in: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ } if (!errors.isOK()) return errors; return Status.OK_STATUS; } @Override public synchronized boolean shouldRun() { return !fRequests.isEmpty(); }
Starts the refresh job
/** * Starts the refresh job */
public void start() { if (Policy.DEBUG_AUTO_REFRESH) Policy.debug(RefreshManager.DEBUG_PREFIX + " enabling auto-refresh"); //$NON-NLS-1$ }
Stops the refresh job
/** * Stops the refresh job */
public void stop() { if (Policy.DEBUG_AUTO_REFRESH) Policy.debug(RefreshManager.DEBUG_PREFIX + " disabling auto-refresh"); //$NON-NLS-1$ cancel(); } }