1 /******************************************************************************* 2 * Copyright (c) 2004, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM - Initial API and implementation 13 * James Blackburn (Broadcom Corp.) - ongoing development 14 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427 15 *******************************************************************************/ 16 package org.eclipse.core.internal.refresh; 17 18 import java.util.*; 19 import org.eclipse.core.internal.events.ILifecycleListener; 20 import org.eclipse.core.internal.events.LifecycleEvent; 21 import org.eclipse.core.internal.resources.Workspace; 22 import org.eclipse.core.internal.utils.Messages; 23 import org.eclipse.core.internal.utils.Policy; 24 import org.eclipse.core.resources.*; 25 import org.eclipse.core.resources.refresh.IRefreshMonitor; 26 import org.eclipse.core.resources.refresh.RefreshProvider; 27 import org.eclipse.core.runtime.*; 28 29 /** 30 * Manages monitors by creating new monitors when projects are added and 31 * removing monitors when projects are removed. Also handles the polling 32 * mechanism when contributed native monitors cannot handle a project. 33 * 34 * @since 3.0 35 */ 36 class MonitorManager implements ILifecycleListener, IPathVariableChangeListener, IResourceChangeListener, IResourceDeltaVisitor { 37 /** 38 * The PollingMonitor in charge of doing file-system polls. 39 */ 40 protected final PollingMonitor pollMonitor; 41 /** 42 * The list of registered monitor factories. This field is guarded by <code>this</code> as 43 * it may be read and written by several threads. 44 */ 45 private RefreshProvider[] providers; 46 /** 47 * Reference to the refresh manager. 48 */ 49 protected final RefreshManager refreshManager; 50 /** 51 * A mapping of monitors to a list of resources each monitor is responsible for. 52 */ 53 protected final Map<IRefreshMonitor, List<IResource>> registeredMonitors; 54 /** 55 * Reference to the workspace. 56 */ 57 protected IWorkspace workspace; 58 MonitorManager(IWorkspace workspace, RefreshManager refreshManager)59 public MonitorManager(IWorkspace workspace, RefreshManager refreshManager) { 60 this.workspace = workspace; 61 this.refreshManager = refreshManager; 62 registeredMonitors = Collections.synchronizedMap(new HashMap<>(10)); 63 pollMonitor = new PollingMonitor(refreshManager); 64 } 65 66 /** 67 * Queries extensions of the refreshProviders extension point, and 68 * creates the provider classes. Will never return <code>null</code>. 69 * 70 * @return RefreshProvider[] The array of registered <code>RefreshProvider</code> 71 * objects or an empty array. 72 */ getRefreshProviders()73 private RefreshProvider[] getRefreshProviders() { 74 synchronized (this) { 75 if (providers != null) 76 return providers; 77 } 78 IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_REFRESH_PROVIDERS); 79 IConfigurationElement[] infos = extensionPoint.getConfigurationElements(); 80 List<RefreshProvider> providerList = new ArrayList<>(infos.length); 81 for (IConfigurationElement configurationElement : infos) { 82 RefreshProvider provider = null; 83 try { 84 provider = (RefreshProvider) configurationElement.createExecutableExtension("class"); //$NON-NLS-1$ 85 } catch (CoreException e) { 86 Policy.log(IStatus.WARNING, Messages.refresh_installError, e); 87 } 88 if (provider != null) 89 providerList.add(provider); 90 } 91 synchronized (this) { 92 providers = providerList.toArray(new RefreshProvider[providerList.size()]); 93 return providers; 94 } 95 } 96 97 /** 98 * Collects the set of root resources that required monitoring. This 99 * includes projects and all linked resources. 100 */ getResourcesToMonitor()101 private List<IResource> getResourcesToMonitor() { 102 final List<IResource> resourcesToMonitor = new ArrayList<>(10); 103 IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN); 104 for (IProject project : projects) { 105 if (!project.isAccessible()) { 106 continue; 107 } 108 resourcesToMonitor.add(project); 109 try { 110 IResource[] members = project.members(); 111 for (IResource member : members) { 112 if (member.isLinked()) 113 resourcesToMonitor.add(member); 114 } 115 }catch (CoreException e) { 116 Policy.log(IStatus.WARNING, Messages.refresh_refreshErr, e); 117 } 118 } 119 return resourcesToMonitor; 120 } 121 122 @Override handleEvent(LifecycleEvent event)123 public void handleEvent(LifecycleEvent event) { 124 switch (event.kind) { 125 case LifecycleEvent.PRE_LINK_DELETE : 126 case LifecycleEvent.PRE_PROJECT_CLOSE : 127 case LifecycleEvent.PRE_PROJECT_DELETE : 128 unmonitor(event.resource, new NullProgressMonitor()); 129 break; 130 } 131 } 132 isMonitoring(IResource resource)133 private boolean isMonitoring(IResource resource) { 134 synchronized (registeredMonitors) { 135 for (List<IResource> resources : registeredMonitors.values()) { 136 if ((resources != null) && (resources.contains(resource))) 137 return true; 138 } 139 } 140 return false; 141 } 142 143 /** 144 * Installs a monitor on the given resource. Returns true if the polling 145 * monitor was installed, and false if a refresh provider was installed. 146 */ monitor(IResource resource, IProgressMonitor progressMonitor)147 boolean monitor(IResource resource, IProgressMonitor progressMonitor) { 148 if (isMonitoring(resource)) 149 return false; 150 boolean pollingMonitorNeeded = true; 151 RefreshProvider[] refreshProviders = getRefreshProviders(); 152 SubMonitor subMonitor = SubMonitor.convert(progressMonitor, refreshProviders.length); 153 for (RefreshProvider refreshProvider : refreshProviders) { 154 IRefreshMonitor monitor = safeInstallMonitor(refreshProvider, resource, subMonitor.split(1)); 155 if (monitor != null) { 156 registerMonitor(monitor, resource); 157 pollingMonitorNeeded = false; 158 } 159 } 160 if (pollingMonitorNeeded) { 161 pollMonitor.monitor(resource); 162 registerMonitor(pollMonitor, resource); 163 } 164 return pollingMonitorNeeded; 165 } 166 167 /* (non-Javadoc) 168 * @see IRefreshResult#monitorFailed 169 */ monitorFailed(IRefreshMonitor monitor, IResource resource)170 public void monitorFailed(IRefreshMonitor monitor, IResource resource) { 171 if (Policy.DEBUG_AUTO_REFRESH) 172 Policy.debug(RefreshManager.DEBUG_PREFIX + " monitor (" + monitor + ") failed to monitor resource: " + resource); //$NON-NLS-1$ //$NON-NLS-2$ 173 if (registeredMonitors == null || monitor == null) 174 return; 175 if (resource == null) { 176 List<IResource> resources = registeredMonitors.get(monitor); 177 if (resources == null || resources.isEmpty()) { 178 registeredMonitors.remove(monitor); 179 return; 180 } 181 // synchronized: protect the collection during iteration 182 synchronized (registeredMonitors) { 183 for (IResource resource1 : resources) { 184 pollMonitor.monitor(resource1); 185 registerMonitor(pollMonitor, resource1); 186 } 187 registeredMonitors.remove(monitor); 188 } 189 } else { 190 removeMonitor(monitor, resource); 191 pollMonitor.monitor(resource); 192 registerMonitor(pollMonitor, resource); 193 } 194 } 195 196 /** 197 * @see IPathVariableChangeListener#pathVariableChanged(IPathVariableChangeEvent) 198 */ 199 @Override pathVariableChanged(IPathVariableChangeEvent event)200 public void pathVariableChanged(IPathVariableChangeEvent event) { 201 if (registeredMonitors.isEmpty()) 202 return; 203 String variableName = event.getVariableName(); 204 final Set<IResource> invalidResources = new HashSet<>(); 205 for (List<IResource> resources : registeredMonitors.values()) { 206 for (IResource resource : resources) { 207 IPath rawLocation = resource.getRawLocation(); 208 if (rawLocation != null) { 209 if (rawLocation.segmentCount() > 0 && variableName.equals(rawLocation.segment(0)) && !invalidResources.contains(resource)) { 210 invalidResources.add(resource); 211 } 212 } 213 } 214 } 215 if (!invalidResources.isEmpty()) { 216 MonitorJob.createSystem(Messages.refresh_restoreOnInvalid, invalidResources, (ICoreRunnable) monitor -> { 217 SubMonitor subMonitor = SubMonitor.convert(monitor, invalidResources.size() * 2); 218 for (IResource resource : invalidResources) { 219 unmonitor(resource, subMonitor.split(1)); 220 monitor(resource, subMonitor.split(1)); 221 // Because the monitor is installed asynchronously we 222 // may have missed some changes, we need to refresh it. 223 refreshManager.refresh(resource); 224 } 225 }).schedule(); 226 } 227 } 228 registerMonitor(IRefreshMonitor monitor, IResource resource)229 private void registerMonitor(IRefreshMonitor monitor, IResource resource) { 230 // synchronized: protect the collection during add 231 synchronized (registeredMonitors) { 232 List<IResource> resources = registeredMonitors.get(monitor); 233 if (resources == null) { 234 resources = new ArrayList<>(1); 235 registeredMonitors.put(monitor, resources); 236 } 237 if (!resources.contains(resource)) 238 resources.add(resource); 239 } 240 if (Policy.DEBUG_AUTO_REFRESH) 241 Policy.debug(RefreshManager.DEBUG_PREFIX + " added monitor (" + monitor + ") on resource: " + resource); //$NON-NLS-1$ //$NON-NLS-2$ 242 } 243 removeMonitor(IRefreshMonitor monitor, IResource resource)244 private void removeMonitor(IRefreshMonitor monitor, IResource resource) { 245 // synchronized: protect the collection during remove 246 synchronized (registeredMonitors) { 247 List<IResource> resources = registeredMonitors.get(monitor); 248 if (resources != null && !resources.isEmpty()) { 249 resources.remove(resource); 250 } else { 251 registeredMonitors.remove(monitor); 252 } 253 } 254 if (Policy.DEBUG_AUTO_REFRESH) 255 Policy.debug(RefreshManager.DEBUG_PREFIX + " removing monitor (" + monitor + ") on resource: " + resource); //$NON-NLS-1$ //$NON-NLS-2$ 256 } 257 safeInstallMonitor(RefreshProvider provider, IResource resource, IProgressMonitor progressMonitor)258 private IRefreshMonitor safeInstallMonitor(RefreshProvider provider, IResource resource, IProgressMonitor progressMonitor) { 259 Throwable t = null; 260 try { 261 return provider.installMonitor(resource, refreshManager, progressMonitor); 262 } catch (Exception | LinkageError e) { 263 t = e; 264 } 265 IStatus error = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, 1, Messages.refresh_installError, t); 266 Policy.log(error); 267 return null; 268 } 269 270 /** 271 * Start the monitoring of resources by all monitors. 272 * @param progressMonitor 273 */ start(IProgressMonitor progressMonitor)274 public void start(IProgressMonitor progressMonitor) { 275 List<IResource> resourcesToMonitor = getResourcesToMonitor(); 276 SubMonitor subMonitor = SubMonitor.convert(progressMonitor, resourcesToMonitor.size() + 1); 277 boolean refreshNeeded = false; 278 for (IResource resource : resourcesToMonitor) { 279 refreshNeeded |= !monitor(resource, subMonitor.split(1)); 280 } 281 workspace.getPathVariableManager().addChangeListener(this); 282 workspace.addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); 283 //adding the lifecycle listener twice does no harm 284 ((Workspace) workspace).addLifecycleListener(this); 285 if (Policy.DEBUG_AUTO_REFRESH) 286 Policy.debug(RefreshManager.DEBUG_PREFIX + " starting monitor manager."); //$NON-NLS-1$ 287 //If not exclusively using polling, create a polling monitor and run it once, to catch 288 //changes that occurred while the native monitor was turned off. 289 subMonitor.split(1); 290 if (refreshNeeded) { 291 new PollingMonitor(refreshManager).runOnce(); 292 } 293 } 294 295 /** 296 * Stop the monitoring of resources by all monitors. 297 */ stop()298 public void stop() { 299 workspace.removeResourceChangeListener(this); 300 workspace.getPathVariableManager().removeChangeListener(this); 301 // synchronized: protect the collection during iteration 302 synchronized (registeredMonitors) { 303 for (IRefreshMonitor monitor : registeredMonitors.keySet()) { 304 monitor.unmonitor(null); 305 } 306 } 307 registeredMonitors.clear(); 308 if (Policy.DEBUG_AUTO_REFRESH) 309 Policy.debug(RefreshManager.DEBUG_PREFIX + " stopping monitor manager."); //$NON-NLS-1$ 310 pollMonitor.cancel(); 311 } 312 unmonitor(IResource resource, IProgressMonitor progressMonitor)313 void unmonitor(IResource resource, IProgressMonitor progressMonitor) { 314 if (resource == null || !isMonitoring(resource)) 315 return; 316 SubMonitor subMonitor = SubMonitor.convert(progressMonitor, 100); 317 synchronized (registeredMonitors) { 318 SubMonitor loopMonitor = subMonitor.split(90).setWorkRemaining(registeredMonitors.entrySet().size()); 319 for (Map.Entry<IRefreshMonitor, List<IResource>> entry : registeredMonitors.entrySet()) { 320 loopMonitor.worked(1); 321 List<IResource> resources = entry.getValue(); 322 if (resources != null && resources.contains(resource)) { 323 entry.getKey().unmonitor(resource); 324 resources.remove(resource); 325 } 326 } 327 } 328 if (resource.getType() == IResource.PROJECT) 329 unmonitorLinkedContents((IProject) resource, subMonitor.split(10)); 330 } 331 unmonitorLinkedContents(IProject project, IProgressMonitor progressMonitor)332 private void unmonitorLinkedContents(IProject project, IProgressMonitor progressMonitor) { 333 if (!project.isAccessible()) 334 return; 335 IResource[] children = null; 336 try { 337 children = project.members(); 338 } catch (CoreException e) { 339 Policy.log(IStatus.WARNING, Messages.refresh_refreshErr, e); 340 } 341 if (children != null && children.length > 0) { 342 SubMonitor subMonitor = SubMonitor.convert(progressMonitor, children.length); 343 for (IResource child : children) { 344 if (child.isLinked()) { 345 unmonitor(child, subMonitor.split(1)); 346 } 347 } 348 } 349 } 350 351 @Override resourceChanged(IResourceChangeEvent event)352 public void resourceChanged(IResourceChangeEvent event) { 353 IResourceDelta delta = event.getDelta(); 354 if (delta == null) 355 return; 356 try { 357 delta.accept(this); 358 } catch (CoreException e) { 359 //cannot happen as our visitor doesn't throw exceptions 360 } 361 } 362 363 @Override visit(IResourceDelta delta)364 public boolean visit(IResourceDelta delta) { 365 if (delta.getKind() == IResourceDelta.ADDED) { 366 IResource resource = delta.getResource(); 367 if (resource.isLinked()) 368 monitor(resource, new NullProgressMonitor()); 369 } 370 if ((delta.getFlags() & IResourceDelta.OPEN) != 0) { 371 IProject project = (IProject) delta.getResource(); 372 if (project.isAccessible()) 373 monitor(project, new NullProgressMonitor()); 374 } 375 return true; 376 } 377 378 } 379