blob: 6abe61272197577cc31ad13a7d4d574382bbfade [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2018 Sonatype, Inc.
* All rights reserved. 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:
* Sonatype, Inc. - initial API and implementation
* Mickael Istria (Red Hat Inc.) - Group operations to save CPU & RAM
*******************************************************************************/
package org.eclipse.m2e.core.internal.project.registry;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.core.resources.IFile;
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.IMavenConfiguration;
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((context, theMonitor) -> {
// group requests
Set<IFile> offlineForceDependencyUpdate = new HashSet<>();
Set<IFile> offlineNotForceDependencyUpdate = new HashSet<>();
Set<IFile> notOfflineForceDependencyUpdate = new HashSet<>();
Set<IFile> notOfflineNotForceDependencyUpdate = new HashSet<>();
for(MavenUpdateRequest request : requests) {
if(theMonitor.isCanceled()) {
throw new OperationCanceledException();
}
if(request.isOffline() && request.isForceDependencyUpdate()) {
offlineForceDependencyUpdate.addAll(request.getPomFiles());
} else if(request.isOffline() && !request.isForceDependencyUpdate()) {
offlineNotForceDependencyUpdate.addAll(request.getPomFiles());
} else if(!request.isOffline() && request.isForceDependencyUpdate()) {
notOfflineForceDependencyUpdate.addAll(request.getPomFiles());
} else if(!request.isOffline() && !request.isForceDependencyUpdate()) {
notOfflineNotForceDependencyUpdate.addAll(request.getPomFiles());
}
}
// process requests
// true * true
if(theMonitor.isCanceled()) {
throw new OperationCanceledException();
}
if(!offlineForceDependencyUpdate.isEmpty()) {
manager.getMaven().execute(true, true, (aContext, aMonitor) -> {
manager.refresh(newState, offlineForceDependencyUpdate, aMonitor);
return null;
}, theMonitor);
}
// true*false
if(theMonitor.isCanceled()) {
throw new OperationCanceledException();
}
if(!offlineNotForceDependencyUpdate.isEmpty()) {
manager.getMaven().execute(true, false, (aContext, aMonitor) -> {
manager.refresh(newState, offlineNotForceDependencyUpdate, aMonitor);
return null;
}, theMonitor);
}
// false*true
if(theMonitor.isCanceled()) {
throw new OperationCanceledException();
}
if(!notOfflineForceDependencyUpdate.isEmpty()) {
manager.getMaven().execute(false, true, (aContext, aMonitor) -> {
manager.refresh(newState, notOfflineForceDependencyUpdate, aMonitor);
return null;
}, theMonitor);
}
// false*false
if(theMonitor.isCanceled()) {
throw new OperationCanceledException();
}
if(!notOfflineNotForceDependencyUpdate.isEmpty()) {
manager.getMaven().execute(false, false, (aContext, aMonitor) -> {
manager.refresh(newState, notOfflineNotForceDependencyUpdate, aMonitor);
return null;
}, theMonitor);
}
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;
}
}