/*
* 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.launch;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.List;
import org.gradle.tooling.LongRunningOperation;
import org.gradle.tooling.ProgressListener;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.gradleware.tooling.toolingmodel.OmniBuildEnvironment;
import com.gradleware.tooling.toolingmodel.repository.FetchStrategy;
import com.gradleware.tooling.toolingmodel.repository.TransientRequestAttributes;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.buildship.core.CorePlugin;
import org.eclipse.buildship.core.GradlePluginsRuntimeException;
import org.eclipse.buildship.core.configuration.BuildConfiguration;
import org.eclipse.buildship.core.configuration.RunConfiguration;
import org.eclipse.buildship.core.configuration.WorkspaceConfiguration;
import org.eclipse.buildship.core.console.ProcessDescription;
import org.eclipse.buildship.core.console.ProcessStreams;
import org.eclipse.buildship.core.event.Event;
import org.eclipse.buildship.core.i18n.CoreMessages;
import org.eclipse.buildship.core.launch.internal.BuildExecutionParticipants;
import org.eclipse.buildship.core.launch.internal.DefaultExecuteLaunchRequestEvent;
import org.eclipse.buildship.core.util.collections.CollectionsUtils;
import org.eclipse.buildship.core.util.file.FileUtils;
import org.eclipse.buildship.core.util.gradle.GradleDistributionFormatter;
import org.eclipse.buildship.core.util.progress.DelegatingProgressListener;
import org.eclipse.buildship.core.util.progress.ToolingApiJob;
import org.eclipse.buildship.core.workspace.GradleBuild;
import org.eclipse.buildship.core.workspace.ModelProvider;
/**
* Base class to execute Gradle builds in a job.
*
* @param <T> the operation type the subclasses can create and execute
*/
public abstract class BaseLaunchRequestJob<T extends LongRunningOperation> extends ToolingApiJob {
protected BaseLaunchRequestJob(String name, boolean notifyUserAboutBuildFailures) {
super(name, notifyUserAboutBuildFailures);
}
@Override
protected final void runToolingApiJob(final IProgressMonitor monitor) throws Exception {
// todo (etst) close streams when done
BuildExecutionParticipants.activateParticipantPlugins();
monitor.beginTask(getJobTaskName(), IProgressMonitor.UNKNOWN);
ProcessDescription processDescription = createProcessDescription();
ProcessStreams processStreams = CorePlugin.processStreamsProvider().createProcessStreams(processDescription);
RunConfiguration runConfig = getRunConfig();
List<ProgressListener> listeners = ImmutableList.<ProgressListener>of(DelegatingProgressListener.withFullOutput(monitor));
TransientRequestAttributes transientAttributes = new TransientRequestAttributes(false, processStreams.getOutput(), processStreams.getError(), processStreams.getInput(),
listeners, Collections.<org.gradle.tooling.events.ProgressListener>emptyList(), getToken());
GradleBuild gradleBuild = CorePlugin.gradleWorkspaceManager().getGradleBuild(runConfig.getBuildConfiguration());
// apply FixedRequestAttributes on build launcher
T launcher = createLaunch(gradleBuild, runConfig, transientAttributes, processDescription);
// let participants add listeners to the build
Event event = new DefaultExecuteLaunchRequestEvent(processDescription, launcher);
CorePlugin.listenerRegistry().dispatch(event);
// print the applied run configuration settings at the beginning of the console output
OutputStreamWriter writer = new OutputStreamWriter(processStreams.getConfiguration());
writeConfig(runConfig, writer, monitor);
// execute the build
executeLaunch(launcher);
}
private void writeConfig(RunConfiguration runConfig, OutputStreamWriter writer, IProgressMonitor monitor) {
BuildConfiguration buildConfig = runConfig.getBuildConfiguration();
WorkspaceConfiguration workspaceConfig = buildConfig.getWorkspaceConfiguration();
OmniBuildEnvironment buildEnvironment = fetchBuildEnvironment(buildConfig, monitor);
// should the user not specify values for the gradleUserHome and javaHome, their default
// values will not be specified in the launch configurations
// as such, these attributes are retrieved separately from the build environment
File gradleUserHome = workspaceConfig.getGradleUserHome();
if (gradleUserHome == null) {
gradleUserHome = buildEnvironment.getGradle().getGradleUserHome().or(null);
}
File javaHome = runConfig.getJavaHome();
if (javaHome == null) {
javaHome = buildEnvironment.getJava().getJavaHome();
}
String gradleVersion = buildEnvironment.getGradle().getGradleVersion();
try {
writer.write(String.format("%s: %s%n", CoreMessages.RunConfiguration_Label_WorkingDirectory, buildConfig.getRootProjectDirectory().getAbsolutePath()));
writer.write(String.format("%s: %s%n", CoreMessages.Preference_Label_GradleUserHome, toNonEmpty(gradleUserHome, CoreMessages.Value_UseGradleDefault)));
writer.write(String.format("%s: %s%n", CoreMessages.RunConfiguration_Label_GradleDistribution, GradleDistributionFormatter.toString(runConfig.getGradleDistribution())));
writer.write(String.format("%s: %s%n", CoreMessages.RunConfiguration_Label_GradleVersion, gradleVersion));
writer.write(String.format("%s: %s%n", CoreMessages.RunConfiguration_Label_JavaHome, toNonEmpty(javaHome, CoreMessages.Value_UseGradleDefault)));
writer.write(String.format("%s: %s%n", CoreMessages.RunConfiguration_Label_JvmArguments, toNonEmpty(runConfig.getJvmArguments(), CoreMessages.Value_None)));
writer.write(String.format("%s: %s%n", CoreMessages.RunConfiguration_Label_Arguments, toNonEmpty(runConfig.getArguments(), CoreMessages.Value_None)));
writeExtraConfigInfo(writer);
writer.write('\n');
writer.flush();
} catch (IOException e) {
throw new GradlePluginsRuntimeException("Cannot write run configuration description to Gradle console.", e);
}
}
private String toNonEmpty(File fileValue, String defaultMessage) {
String string = FileUtils.getAbsolutePath(fileValue).orNull();
return string != null ? string : defaultMessage;
}
private String toNonEmpty(List<String> stringValues, String defaultMessage) {
String string = Strings.emptyToNull(CollectionsUtils.joinWithSpace(stringValues));
return string != null ? string : defaultMessage;
}
private OmniBuildEnvironment fetchBuildEnvironment(BuildConfiguration buildConfig, IProgressMonitor monitor) {
ModelProvider modelProvider = CorePlugin.gradleWorkspaceManager().getGradleBuild(buildConfig).getModelProvider();
return modelProvider.fetchBuildEnvironment(FetchStrategy.FORCE_RELOAD, getToken(), monitor);
}
/**
* The name of the job to display in the progress view.
*
* @return the name of the job
*/
protected abstract String getJobTaskName();
/**
* The run configuration attributes to apply when executing the request.
*
* @return the run configuration attributes
*/
protected abstract RunConfiguration getRunConfig();
/**
* The process description.
*
* @return the process description
*/
protected abstract ProcessDescription createProcessDescription();
/**
* Creates a new launcher object to execute in the job.
*
* @return the new launcher
*/
protected abstract T createLaunch(GradleBuild gradleBuild, RunConfiguration runConfiguration, TransientRequestAttributes transientAttributes, ProcessDescription processDescription);
/**
* Execute the launcher created by {@code #createLaunch()}.
*
* @param launcher the launcher to execute
*/
protected abstract void executeLaunch(T launcher);
/**
* Writes extra information on the configuration console.
*
* @param writer the writer to print messages with
* @throws IOException if an exception happens when writing a message
*/
protected abstract void writeExtraConfigInfo(OutputStreamWriter writer) throws IOException;
/**
* Convenience implementation of the ProcessDescription interface.
*/
protected abstract static class BaseProcessDescription implements ProcessDescription {
private final String name;
private final Job job;
private final RunConfiguration runConfig;
protected BaseProcessDescription(String name, Job job, RunConfiguration runConfig) {
this.name = Preconditions.checkNotNull(name);
this.job = Preconditions.checkNotNull(job);
this.runConfig = Preconditions.checkNotNull(runConfig);
}
@Override
public String getName() {
return this.name;
}
@Override
public Job getJob() {
return this.job;
}
@Override
public RunConfiguration getRunConfig() {
return this.runConfig;
}
}
}