/******************************************************************************* * Copyright (c) 2004, 2011 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 *******************************************************************************/ 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.*; /** * 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. */ 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() { 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<RefreshProvider>(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); } 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<IResource>(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; } public void handleEvent(LifecycleEvent event) { switch (event.kind) { case LifecycleEvent.PRE_LINK_DELETE: case LifecycleEvent.PRE_PROJECT_CLOSE: case LifecycleEvent.PRE_PROJECT_DELETE: unmonitor(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) { if (isMonitoring(resource)) return false; boolean pollingMonitorNeeded = true; RefreshProvider[] refreshProviders = getRefreshProviders(); for (int i = 0; i < refreshProviders.length; i++) { IRefreshMonitor monitor = safeInstallMonitor(refreshProviders[i], resource); 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 (RefreshManager.DEBUG) System.err.println(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) */ public void pathVariableChanged(IPathVariableChangeEvent event) { if (registeredMonitors.isEmpty()) return; String variableName = event.getVariableName(); Set<IResource> invalidResources = new HashSet<IResource>(); 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()) { for (Iterator<IResource> i = invalidResources.iterator(); i.hasNext();) { IResource resource = i.next(); unmonitor(resource); monitor(resource); } } } 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<IResource>(1); registeredMonitors.put(monitor, resources); } if (!resources.contains(resource)) resources.add(resource); } if (RefreshManager.DEBUG) System.out.println(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 (RefreshManager.DEBUG) System.out.println(RefreshManager.DEBUG_PREFIX + " removing monitor (" + monitor + ") on resource: " + resource); //$NON-NLS-1$ //$NON-NLS-2$ } private IRefreshMonitor safeInstallMonitor(RefreshProvider provider, IResource resource) { Throwable t = null; try { return provider.installMonitor(resource, refreshManager); } 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. */ public void start() { boolean refreshNeeded = false; for (Iterator<IResource> i = getResourcesToMonitor().iterator(); i.hasNext();) refreshNeeded |= !monitor(i.next()); workspace.getPathVariableManager().addChangeListener(this); workspace.addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); //adding the lifecycle listener twice does no harm ((Workspace)workspace).addLifecycleListener(this); if (RefreshManager.DEBUG) System.out.println(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(); } /** * 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 (RefreshManager.DEBUG) System.out.println(RefreshManager.DEBUG_PREFIX + " stopping monitor manager."); //$NON-NLS-1$ pollMonitor.cancel(); } void unmonitor(IResource resource) { if (resource == null || !isMonitoring(resource)) return; synchronized (registeredMonitors) { for (Iterator<Entry<IRefreshMonitor, List<IResource>>> i = registeredMonitors.entrySet().iterator(); i.hasNext();) { 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); } private void unmonitorLinkedContents(IProject project) { 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) for (int i = 0; i < children.length; i++) if (children[i].isLinked()) unmonitor(children[i]); } 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 } } public boolean visit(IResourceDelta delta) { if (delta.getKind () == IResourceDelta.ADDED) { IResource resource = delta.getResource(); if (resource.isLinked()) monitor(resource); } if ((delta.getFlags() & IResourceDelta.OPEN) != 0) { IProject project = (IProject) delta.getResource(); if (project.isAccessible()) monitor(project); } return true; } }