| /******************************************************************************* |
| * Copyright (c) 2004, 2015 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| * James Blackburn (Broadcom Corp.) - ongoing development |
| * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427 |
| *******************************************************************************/ |
| package org.eclipse.core.internal.refresh; |
| |
| import java.util.*; |
| import java.util.Map.Entry; |
| import org.eclipse.core.internal.events.ILifecycleListener; |
| import org.eclipse.core.internal.events.LifecycleEvent; |
| import org.eclipse.core.internal.resources.Workspace; |
| import org.eclipse.core.internal.utils.Messages; |
| import org.eclipse.core.internal.utils.Policy; |
| import org.eclipse.core.resources.*; |
| import org.eclipse.core.resources.refresh.IRefreshMonitor; |
| import org.eclipse.core.resources.refresh.RefreshProvider; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.osgi.util.NLS; |
| |
| /** |
| * Manages monitors by creating new monitors when projects are added and |
| * removing monitors when projects are removed. Also handles the polling |
| * mechanism when contributed native monitors cannot handle a project. |
| * |
| * @since 3.0 |
| */ |
| class MonitorManager implements ILifecycleListener, IPathVariableChangeListener, IResourceChangeListener, IResourceDeltaVisitor { |
| /** |
| * The PollingMonitor in charge of doing file-system polls. |
| */ |
| protected final PollingMonitor pollMonitor; |
| /** |
| * The list of registered monitor factories. This field is guarded by <code>this</code> as |
| * it may be read and written by several threads. |
| */ |
| private RefreshProvider[] providers; |
| /** |
| * Reference to the refresh manager. |
| */ |
| protected final RefreshManager refreshManager; |
| /** |
| * A mapping of monitors to a list of resources each monitor is responsible for. |
| */ |
| protected final Map<IRefreshMonitor, List<IResource>> registeredMonitors; |
| /** |
| * Reference to the workspace. |
| */ |
| protected IWorkspace workspace; |
| |
| public MonitorManager(IWorkspace workspace, RefreshManager refreshManager) { |
| this.workspace = workspace; |
| this.refreshManager = refreshManager; |
| registeredMonitors = Collections.synchronizedMap(new HashMap<IRefreshMonitor, List<IResource>>(10)); |
| pollMonitor = new PollingMonitor(refreshManager); |
| } |
| |
| /** |
| * Queries extensions of the refreshProviders extension point, and |
| * creates the provider classes. Will never return <code>null</code>. |
| * |
| * @return RefreshProvider[] The array of registered <code>RefreshProvider</code> |
| * objects or an empty array. |
| */ |
| private RefreshProvider[] getRefreshProviders() { |
| synchronized (this) { |
| if (providers != null) |
| return providers; |
| } |
| IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_REFRESH_PROVIDERS); |
| IConfigurationElement[] infos = extensionPoint.getConfigurationElements(); |
| List<RefreshProvider> providerList = new ArrayList<>(infos.length); |
| for (int i = 0; i < infos.length; i++) { |
| IConfigurationElement configurationElement = infos[i]; |
| RefreshProvider provider = null; |
| try { |
| provider = (RefreshProvider) configurationElement.createExecutableExtension("class"); //$NON-NLS-1$ |
| } catch (CoreException e) { |
| Policy.log(IStatus.WARNING, Messages.refresh_installError, e); |
| } |
| if (provider != null) |
| providerList.add(provider); |
| } |
| synchronized (this) { |
| providers = providerList.toArray(new RefreshProvider[providerList.size()]); |
| return providers; |
| } |
| } |
| |
| /** |
| * Collects the set of root resources that required monitoring. This |
| * includes projects and all linked resources. |
| */ |
| private List<IResource> getResourcesToMonitor() { |
| final List<IResource> resourcesToMonitor = new ArrayList<>(10); |
| IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN); |
| for (int i = 0; i < projects.length; i++) { |
| if (!projects[i].isAccessible()) |
| continue; |
| resourcesToMonitor.add(projects[i]); |
| try { |
| IResource[] members = projects[i].members(); |
| for (int j = 0; j < members.length; j++) |
| if (members[j].isLinked()) |
| resourcesToMonitor.add(members[j]); |
| } catch (CoreException e) { |
| Policy.log(IStatus.WARNING, Messages.refresh_refreshErr, e); |
| } |
| } |
| return resourcesToMonitor; |
| } |
| |
| @Override |
| public void handleEvent(LifecycleEvent event) { |
| switch (event.kind) { |
| case LifecycleEvent.PRE_LINK_DELETE : |
| case LifecycleEvent.PRE_PROJECT_CLOSE : |
| case LifecycleEvent.PRE_PROJECT_DELETE : |
| unmonitorAsync(event.resource); |
| break; |
| } |
| } |
| |
| private boolean isMonitoring(IResource resource) { |
| synchronized (registeredMonitors) { |
| for (Iterator<IRefreshMonitor> i = registeredMonitors.keySet().iterator(); i.hasNext();) { |
| List<IResource> resources = registeredMonitors.get(i.next()); |
| if ((resources != null) && (resources.contains(resource))) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Installs a monitor on the given resource. Returns true if the polling |
| * monitor was installed, and false if a refresh provider was installed. |
| */ |
| boolean monitor(IResource resource, IProgressMonitor progressMonitor) { |
| if (isMonitoring(resource)) |
| return false; |
| boolean pollingMonitorNeeded = true; |
| RefreshProvider[] refreshProviders = getRefreshProviders(); |
| SubMonitor subMonitor = SubMonitor.convert(progressMonitor, refreshProviders.length); |
| for (int i = 0; i < refreshProviders.length; i++) { |
| IRefreshMonitor monitor = safeInstallMonitor(refreshProviders[i], resource, subMonitor.split(1)); |
| if (monitor != null) { |
| registerMonitor(monitor, resource); |
| pollingMonitorNeeded = false; |
| } |
| } |
| if (pollingMonitorNeeded) { |
| pollMonitor.monitor(resource); |
| registerMonitor(pollMonitor, resource); |
| } |
| return pollingMonitorNeeded; |
| } |
| |
| /* (non-Javadoc) |
| * @see IRefreshResult#monitorFailed |
| */ |
| public void monitorFailed(IRefreshMonitor monitor, IResource resource) { |
| if (Policy.DEBUG_AUTO_REFRESH) |
| Policy.debug(RefreshManager.DEBUG_PREFIX + " monitor (" + monitor + ") failed to monitor resource: " + resource); //$NON-NLS-1$ //$NON-NLS-2$ |
| if (registeredMonitors == null || monitor == null) |
| return; |
| if (resource == null) { |
| List<IResource> resources = registeredMonitors.get(monitor); |
| if (resources == null || resources.isEmpty()) { |
| registeredMonitors.remove(monitor); |
| return; |
| } |
| // synchronized: protect the collection during iteration |
| synchronized (registeredMonitors) { |
| for (Iterator<IResource> i = resources.iterator(); i.hasNext();) { |
| resource = i.next(); |
| pollMonitor.monitor(resource); |
| registerMonitor(pollMonitor, resource); |
| } |
| registeredMonitors.remove(monitor); |
| } |
| } else { |
| removeMonitor(monitor, resource); |
| pollMonitor.monitor(resource); |
| registerMonitor(pollMonitor, resource); |
| } |
| } |
| |
| /** |
| * @see org.eclipse.core.resources.IPathVariableChangeListener#pathVariableChanged(org.eclipse.core.resources.IPathVariableChangeEvent) |
| */ |
| @Override |
| public void pathVariableChanged(IPathVariableChangeEvent event) { |
| if (registeredMonitors.isEmpty()) |
| return; |
| String variableName = event.getVariableName(); |
| final Set<IResource> invalidResources = new HashSet<>(); |
| for (Iterator<List<IResource>> i = registeredMonitors.values().iterator(); i.hasNext();) { |
| for (Iterator<IResource> j = i.next().iterator(); j.hasNext();) { |
| IResource resource = j.next(); |
| IPath rawLocation = resource.getRawLocation(); |
| if (rawLocation != null) { |
| if (rawLocation.segmentCount() > 0 && variableName.equals(rawLocation.segment(0)) && !invalidResources.contains(resource)) { |
| invalidResources.add(resource); |
| } |
| } |
| } |
| } |
| if (!invalidResources.isEmpty()) { |
| MonitorJob.createSystem(Messages.refresh_restoreOnInvalid, invalidResources, new ICoreRunnable() { |
| @Override |
| public void run(IProgressMonitor monitor) { |
| SubMonitor subMonitor = SubMonitor.convert(monitor, invalidResources.size() * 2); |
| for (Iterator<IResource> i = invalidResources.iterator(); i.hasNext();) { |
| IResource resource = i.next(); |
| unmonitor(resource, subMonitor.split(1)); |
| monitor(resource, subMonitor.split(1)); |
| // Because the monitor is installed asynchronously we |
| // may have missed some changes, we need to refresh it. |
| refreshManager.refresh(resource); |
| } |
| } |
| }).schedule(); |
| } |
| } |
| |
| private void registerMonitor(IRefreshMonitor monitor, IResource resource) { |
| // synchronized: protect the collection during add |
| synchronized (registeredMonitors) { |
| List<IResource> resources = registeredMonitors.get(monitor); |
| if (resources == null) { |
| resources = new ArrayList<>(1); |
| registeredMonitors.put(monitor, resources); |
| } |
| if (!resources.contains(resource)) |
| resources.add(resource); |
| } |
| if (Policy.DEBUG_AUTO_REFRESH) |
| Policy.debug(RefreshManager.DEBUG_PREFIX + " added monitor (" + monitor + ") on resource: " + resource); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| private void removeMonitor(IRefreshMonitor monitor, IResource resource) { |
| // synchronized: protect the collection during remove |
| synchronized (registeredMonitors) { |
| List<IResource> resources = registeredMonitors.get(monitor); |
| if (resources != null && !resources.isEmpty()) |
| resources.remove(resource); |
| else |
| registeredMonitors.remove(monitor); |
| } |
| if (Policy.DEBUG_AUTO_REFRESH) |
| Policy.debug(RefreshManager.DEBUG_PREFIX + " removing monitor (" + monitor + ") on resource: " + resource); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| private IRefreshMonitor safeInstallMonitor(RefreshProvider provider, IResource resource, IProgressMonitor progressMonitor) { |
| Throwable t = null; |
| try { |
| return provider.installMonitor(resource, refreshManager, progressMonitor); |
| } catch (Exception e) { |
| t = e; |
| } catch (LinkageError e) { |
| t = e; |
| } |
| IStatus error = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, 1, Messages.refresh_installError, t); |
| Policy.log(error); |
| return null; |
| } |
| |
| /** |
| * Start the monitoring of resources by all monitors. |
| * @param progressMonitor |
| */ |
| public void start(IProgressMonitor progressMonitor) { |
| List<IResource> resourcesToMonitor = getResourcesToMonitor(); |
| SubMonitor subMonitor = SubMonitor.convert(progressMonitor, resourcesToMonitor.size() + 1); |
| boolean refreshNeeded = false; |
| for (Iterator<IResource> i = resourcesToMonitor.iterator(); i.hasNext();) |
| refreshNeeded |= !monitor(i.next(), subMonitor.split(1)); |
| workspace.getPathVariableManager().addChangeListener(this); |
| workspace.addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); |
| //adding the lifecycle listener twice does no harm |
| ((Workspace) workspace).addLifecycleListener(this); |
| if (Policy.DEBUG_AUTO_REFRESH) |
| Policy.debug(RefreshManager.DEBUG_PREFIX + " starting monitor manager."); //$NON-NLS-1$ |
| //If not exclusively using polling, create a polling monitor and run it once, to catch |
| //changes that occurred while the native monitor was turned off. |
| if (refreshNeeded) { |
| new PollingMonitor(refreshManager).runOnce(); |
| } |
| subMonitor.split(1); |
| } |
| |
| /** |
| * Stop the monitoring of resources by all monitors. |
| */ |
| public void stop() { |
| workspace.removeResourceChangeListener(this); |
| workspace.getPathVariableManager().removeChangeListener(this); |
| // synchronized: protect the collection during iteration |
| synchronized (registeredMonitors) { |
| for (Iterator<IRefreshMonitor> i = registeredMonitors.keySet().iterator(); i.hasNext();) { |
| IRefreshMonitor monitor = i.next(); |
| monitor.unmonitor(null); |
| } |
| } |
| registeredMonitors.clear(); |
| if (Policy.DEBUG_AUTO_REFRESH) |
| Policy.debug(RefreshManager.DEBUG_PREFIX + " stopping monitor manager."); //$NON-NLS-1$ |
| pollMonitor.cancel(); |
| } |
| |
| void unmonitor(IResource resource, IProgressMonitor progressMonitor) { |
| if (resource == null || !isMonitoring(resource)) |
| return; |
| SubMonitor subMonitor = SubMonitor.convert(progressMonitor, 100); |
| synchronized (registeredMonitors) { |
| SubMonitor loopMonitor = subMonitor.split(90).setWorkRemaining(registeredMonitors.entrySet().size()); |
| for (Iterator<Entry<IRefreshMonitor, List<IResource>>> i = registeredMonitors.entrySet().iterator(); i.hasNext();) { |
| loopMonitor.worked(1); |
| Entry<IRefreshMonitor, List<IResource>> current = i.next(); |
| List<IResource> resources = current.getValue(); |
| if ((resources != null) && !resources.isEmpty() && resources.contains(resource)) { |
| current.getKey().unmonitor(resource); |
| resources.remove(resource); |
| } |
| } |
| } |
| if (resource.getType() == IResource.PROJECT) |
| unmonitorLinkedContents((IProject) resource, subMonitor.split(10)); |
| } |
| |
| private void unmonitorLinkedContents(IProject project, IProgressMonitor progressMonitor) { |
| if (!project.isAccessible()) |
| return; |
| IResource[] children = null; |
| try { |
| children = project.members(); |
| } catch (CoreException e) { |
| Policy.log(IStatus.WARNING, Messages.refresh_refreshErr, e); |
| } |
| |
| if (children != null && children.length > 0) { |
| SubMonitor subMonitor = SubMonitor.convert(progressMonitor, children.length); |
| for (int i = 0; i < children.length; i++) { |
| if (children[i].isLinked()) { |
| unmonitor(children[i], subMonitor.split(1)); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void resourceChanged(IResourceChangeEvent event) { |
| IResourceDelta delta = event.getDelta(); |
| if (delta == null) |
| return; |
| try { |
| delta.accept(this); |
| } catch (CoreException e) { |
| //cannot happen as our visitor doesn't throw exceptions |
| } |
| } |
| |
| @Override |
| public boolean visit(IResourceDelta delta) { |
| if (delta.getKind() == IResourceDelta.ADDED) { |
| IResource resource = delta.getResource(); |
| if (resource.isLinked()) |
| monitorAsync(resource); |
| } |
| if ((delta.getFlags() & IResourceDelta.OPEN) != 0) { |
| IProject project = (IProject) delta.getResource(); |
| if (project.isAccessible()) |
| monitorAsync(project); |
| } |
| return true; |
| } |
| |
| private void monitorAsync(final IResource resource) { |
| MonitorJob.createSystem(NLS.bind(Messages.refresh_installMonitor, resource), resource, new ICoreRunnable() { |
| @Override |
| public void run(IProgressMonitor monitor) { |
| monitor(resource, monitor); |
| // Because the monitor is installed asynchronously we |
| // may have missed some changes, we need to refresh it. |
| refreshManager.refresh(resource); |
| } |
| }).schedule(); |
| } |
| |
| private void unmonitorAsync(final IResource resource) { |
| MonitorJob.createSystem(NLS.bind(Messages.refresh_uninstallMonitor, resource), resource, new ICoreRunnable() { |
| @Override |
| public void run(IProgressMonitor monitor) { |
| unmonitor(resource, monitor); |
| } |
| }).schedule(); |
| } |
| } |