/* * Copyright (c) 2015 the original author or authors. * 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: * Etienne Studer & Donát Csikós (Gradle Inc.) - initial API and implementation and initial documentation */ package org.eclipse.buildship.core.util.progress; import java.util.concurrent.TimeUnit; import org.gradle.tooling.CancellationToken; import org.gradle.tooling.CancellationTokenSource; import org.gradle.tooling.GradleConnector; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.buildship.core.CorePlugin; /** * Base class for cancellable workspace jobs that invoke the Gradle Tooling API. * * Note that if the job returns a normal error status then Eclipse shows the default error dialog, * which is too basic for our needs. On the other hand, the OK_STATUS is not feasible since invokers * of the job might need to determine if the job has finished successfully or not. To solve this * dilemma, we return an INFO/CANCEL status containing the thrown exception. */ public abstract class ToolingApiWorkspaceJob extends WorkspaceJob { private final CancellationTokenSource tokenSource; private final String workName; private final boolean notifyUserAboutBuildFailures; /** * Creates a new job with the specified name. The job name is a human-readable value that is * displayed to users. The name does not need to be unique, but it must not be {@code null}. A * token for Gradle build cancellation is created. * * @param name the name of the job */ protected ToolingApiWorkspaceJob(String name) { this(name, true); } /** * Creates a new job with the specified name. The job name is a human-readable value that is * displayed to users. The name does not need to be unique, but it must not be {@code null}. A * token for Gradle build cancellation is created. * * @param name the name of the job * @param notifyUserAboutBuildFailures {@code true} if the user should be visually notified about build failures that happen while running the job */ protected ToolingApiWorkspaceJob(String name, boolean notifyUserAboutBuildFailures) { super(name); this.tokenSource = GradleConnector.newCancellationTokenSource(); this.workName = name; this.notifyUserAboutBuildFailures = notifyUserAboutBuildFailures; } protected CancellationToken getToken() { return this.tokenSource.token(); } @Override public final IStatus runInWorkspace(final IProgressMonitor monitor) throws CoreException { ToolingApiInvoker invoker = new ToolingApiInvoker(this.workName, this.notifyUserAboutBuildFailures); final IProgressMonitor efficientMonitor = new RateLimitingProgressMonitor(monitor, 500, TimeUnit.MILLISECONDS); return invoker.invoke(new ToolingApiCommand() { @Override public void run() throws Exception { runToolingApiJobInWorkspace(efficientMonitor); } }, efficientMonitor); } /** * Template method that executes the actual Tooling API-related work. * <p/> * If an exception is thrown in this method, the exception is reported via the * {@link org.eclipse.buildship.core.notification.UserNotification} service. The * notification content and its severity depend on the type of the thrown exception. * <p/> * If no exception is thrown in the template method, the job's return status is * {@link org.eclipse.core.runtime.Status#OK_STATUS}. If an exception occurs, a non-error, non-ok status * is returned. This disables the platform UI to show the built-in (and rather basic) * exception dialog. * * @param monitor the monitor to report the progress * @throws Exception thrown when an error happens during the execution */ protected abstract void runToolingApiJobInWorkspace(IProgressMonitor monitor) throws Exception; @Override protected void canceling() { this.tokenSource.cancel(); } @Override public boolean belongsTo(Object family) { return CorePlugin.GRADLE_JOB_FAMILY.equals(family); } }