/******************************************************************************* * Copyright (c) 2008-2010 Sonatype, Inc. * 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: * Sonatype, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.m2e.core.internal.project.registry; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.m2e.core.embedder.ICallable; import org.eclipse.m2e.core.embedder.IMavenConfiguration; import org.eclipse.m2e.core.embedder.IMavenExecutionContext; import org.eclipse.m2e.core.internal.IMavenConstants; import org.eclipse.m2e.core.internal.Messages; import org.eclipse.m2e.core.internal.jobs.IBackgroundProcessingQueue; import org.eclipse.m2e.core.project.MavenUpdateRequest; public class ProjectRegistryRefreshJob extends Job implements IResourceChangeListener, IPreferenceChangeListener, IBackgroundProcessingQueue { private static final Logger log = LoggerFactory.getLogger(ProjectRegistryRefreshJob.class); private static final long SCHEDULE_DELAY = 1000L; private final Set<MavenUpdateRequest> queue = new LinkedHashSet<MavenUpdateRequest>(); /*package*/final ProjectRegistryManager manager; private final IMavenConfiguration mavenConfiguration; public ProjectRegistryRefreshJob(ProjectRegistryManager manager, IMavenConfiguration mavenConfiguration) { super(Messages.ProjectRegistryRefreshJob_title); this.manager = manager; this.mavenConfiguration = mavenConfiguration; } public void refresh(MavenUpdateRequest updateRequest) { queue(updateRequest); schedule(SCHEDULE_DELAY); } // Job public IStatus run(final IProgressMonitor monitor) { monitor.beginTask(Messages.ProjectRegistryRefreshJob_task_refreshing, IProgressMonitor.UNKNOWN); final ArrayList<MavenUpdateRequest> requests; synchronized(this.queue) { requests = new ArrayList<MavenUpdateRequest>(this.queue); this.queue.clear(); } try { final MutableProjectRegistry newState = manager.newMutableProjectRegistry(); try { manager.getMaven().execute(new ICallable<Void>() { public Void call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException { for(final MavenUpdateRequest request : requests) { if(monitor.isCanceled()) { throw new OperationCanceledException(); } manager.getMaven().execute(request.isOffline(), request.isForceDependencyUpdate(), new ICallable<Void>() { public Void call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException { manager.refresh(newState, request.getPomFiles(), monitor); return null; } }, monitor); } ISchedulingRule rule = ResourcesPlugin.getWorkspace().getRoot(); getJobManager().beginRule(rule, monitor); try { manager.applyMutableProjectRegistry(newState, monitor); } finally { getJobManager().endRule(rule); } return null; } }, monitor); } finally { newState.close(); } } catch(CoreException ex) { log.error(ex.getMessage(), ex); } catch(OperationCanceledException ex) { log.info("{} was canceled", getClass().getName()); } catch(StaleMutableProjectRegistryException e) { synchronized(this.queue) { // must preserve order of requests here requests.addAll(this.queue); this.queue.clear(); this.queue.addAll(requests); if(!this.queue.isEmpty()) { schedule(SCHEDULE_DELAY); } } } catch(Exception ex) { log.error(ex.getMessage(), ex); } finally { monitor.done(); } return Status.OK_STATUS; } // IResourceChangeListener public void resourceChanged(IResourceChangeEvent event) { boolean offline = mavenConfiguration.isOffline(); boolean forceDependencyUpdate = false; int type = event.getType(); if(IResourceChangeEvent.PRE_CLOSE == type || IResourceChangeEvent.PRE_DELETE == type) { IProject project = (IProject) event.getResource(); if(isMavenProject(project)) { queue(new MavenUpdateRequest(project, offline, forceDependencyUpdate)); } } else { // if (IResourceChangeEvent.POST_CHANGE == type) // MavenBuilder will synchronously read/refresh workspace Maven project state. // We still refresh opened projects because workspace does not run build after project open event. IResourceDelta delta = event.getDelta(); // workspace delta IResourceDelta[] projectDeltas = delta.getAffectedChildren(); for(int i = 0; i < projectDeltas.length; i++ ) { IResourceDelta projectDelta = projectDeltas[i]; IProject project = (IProject) projectDelta.getResource(); if(!isMavenProject(project)) { continue; } //Bug 436679: queue update request only for reopened projects. //Imported projects (delta.getKind() == IResourceDelta.ADDED) will be taken care of by the builder. if((projectDelta.getKind() == IResourceDelta.CHANGED && (projectDelta.getFlags() & IResourceDelta.OPEN) != 0)) { queue(new MavenUpdateRequest(project, offline, forceDependencyUpdate)); } } } synchronized(queue) { if(!queue.isEmpty()) { schedule(SCHEDULE_DELAY); } } } private void queue(MavenUpdateRequest updateRequest) { synchronized(queue) { queue.add(updateRequest); log.debug("Queued refresh request: {}", updateRequest.toString()); //$NON-NLS-1$ } } public void preferenceChange(PreferenceChangeEvent event) { boolean offline = mavenConfiguration.isOffline(); boolean updateSnapshots = false; if(event.getSource() instanceof IProject) { queue(new MavenUpdateRequest((IProject) event.getSource(), offline, updateSnapshots)); } } public boolean isEmpty() { synchronized(queue) { return queue.isEmpty(); } } private boolean isMavenProject(IProject project) { try { return project != null && project.isAccessible() && project.hasNature(IMavenConstants.NATURE_ID); } catch(CoreException ex) { log.error(ex.getMessage(), ex); } return false; } }