/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.tooling.internal.provider.runner;
import org.gradle.api.Task;
import org.gradle.api.execution.internal.ExecuteTaskBuildOperationDetails;
import org.gradle.api.internal.TaskInternal;
import org.gradle.api.internal.tasks.TaskStateInternal;
import org.gradle.initialization.BuildEventConsumer;
import org.gradle.internal.progress.BuildOperationDescriptor;
import org.gradle.internal.progress.BuildOperationListener;
import org.gradle.internal.progress.OperationFinishEvent;
import org.gradle.internal.progress.OperationStartEvent;
import org.gradle.tooling.internal.provider.BuildClientSubscriptions;
import org.gradle.tooling.internal.provider.events.AbstractTaskResult;
import org.gradle.tooling.internal.provider.events.DefaultFailure;
import org.gradle.tooling.internal.provider.events.DefaultTaskDescriptor;
import org.gradle.tooling.internal.provider.events.DefaultTaskFailureResult;
import org.gradle.tooling.internal.provider.events.DefaultTaskFinishedProgressEvent;
import org.gradle.tooling.internal.provider.events.DefaultTaskSkippedResult;
import org.gradle.tooling.internal.provider.events.DefaultTaskStartedProgressEvent;
import org.gradle.tooling.internal.provider.events.DefaultTaskSuccessResult;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Task listener that forwards all receiving events to the client via the provided {@code BuildEventConsumer} instance.
*
* @since 2.5
*/
class ClientForwardingTaskOperationListener implements BuildOperationListener {
private final BuildEventConsumer eventConsumer;
private final BuildClientSubscriptions clientSubscriptions;
private final BuildOperationListener delegate;
private final Set<Object> skipEvents = new HashSet<Object>();
ClientForwardingTaskOperationListener(BuildEventConsumer eventConsumer, BuildClientSubscriptions clientSubscriptions, BuildOperationListener delegate) {
this.eventConsumer = eventConsumer;
this.clientSubscriptions = clientSubscriptions;
this.delegate = delegate;
}
@Override
public void started(BuildOperationDescriptor buildOperation, OperationStartEvent startEvent) {
if (skipEvents.contains(buildOperation.getParentId())) {
skipEvents.add(buildOperation.getId());
return;
}
if (buildOperation.getDetails() instanceof ExecuteTaskBuildOperationDetails) {
if (clientSubscriptions.isSendTaskProgressEvents()) {
Task task = ((ExecuteTaskBuildOperationDetails) buildOperation.getDetails()).getTask();
eventConsumer.dispatch(new DefaultTaskStartedProgressEvent(startEvent.getStartTime(), toTaskDescriptor(buildOperation, (TaskInternal) task)));
} else {
// Discard this operation and all children
skipEvents.add(buildOperation.getId());
}
} else {
delegate.started(buildOperation, startEvent);
}
}
@Override
public void finished(BuildOperationDescriptor buildOperation, OperationFinishEvent finishEvent) {
if (skipEvents.remove(buildOperation.getId())) {
return;
}
if (buildOperation.getDetails() instanceof ExecuteTaskBuildOperationDetails) {
Task task = ((ExecuteTaskBuildOperationDetails) buildOperation.getDetails()).getTask();
TaskInternal taskInternal = (TaskInternal) task;
eventConsumer.dispatch(new DefaultTaskFinishedProgressEvent(finishEvent.getEndTime(), toTaskDescriptor(buildOperation, taskInternal), toTaskResult(taskInternal, finishEvent)));
} else {
delegate.finished(buildOperation, finishEvent);
}
}
private DefaultTaskDescriptor toTaskDescriptor(BuildOperationDescriptor buildOperation, TaskInternal task) {
Object id = buildOperation.getId();
String taskIdentityPath = buildOperation.getName();
String displayName = buildOperation.getDisplayName();
String taskPath = task.getIdentityPath().toString();
Object parentId = getParentId(buildOperation);
return new DefaultTaskDescriptor(id, taskIdentityPath, taskPath, displayName, parentId);
}
private Object getParentId(BuildOperationDescriptor buildOperation) {
// only set the BuildOperation as the parent if the Tooling API Consumer is listening to build progress events
return clientSubscriptions.isSendBuildProgressEvents() ? buildOperation.getParentId() : null;
}
private static AbstractTaskResult toTaskResult(TaskInternal task, OperationFinishEvent result) {
TaskStateInternal state = task.getState();
long startTime = result.getStartTime();
long endTime = result.getEndTime();
if (state.getUpToDate()) {
return new DefaultTaskSuccessResult(startTime, endTime, true, state.isFromCache(), state.getSkipMessage());
} else if (state.getSkipped()) {
return new DefaultTaskSkippedResult(startTime, endTime, state.getSkipMessage());
} else {
Throwable failure = result.getFailure();
if (failure == null) {
return new DefaultTaskSuccessResult(startTime, endTime, false, state.isFromCache(), "SUCCESS");
} else {
return new DefaultTaskFailureResult(startTime, endTime, Collections.singletonList(DefaultFailure.fromThrowable(failure)));
}
}
}
}