/*
* Copyright 2009 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.foundation.ipc.gradle;
import org.gradle.BuildAdapter;
import org.gradle.BuildResult;
import org.gradle.api.invocation.Gradle;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.api.logging.StandardOutputListener;
import org.gradle.foundation.ProjectConverter;
import org.gradle.foundation.ProjectView;
import org.gradle.foundation.ipc.basic.ClientProcess;
import org.gradle.foundation.ipc.basic.MessageObject;
import org.gradle.foundation.ipc.basic.Server;
import org.gradle.gradleplugin.foundation.GradlePluginLord;
import java.io.Serializable;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* This manages the communication between the UI and an externally-launched copy
* of Gradle when using socket-based inter-process communication. This is the
* client (gradle) side used to build a task list (tree actually). We add gradle
* listeners and send their notifications as messages back to the server.
*
* @author mhunsicker
*/
public class TaskListClientProtocol implements ClientProcess.Protocol {
private final Logger logger = Logging.getLogger(TaskListClientProtocol.class);
private ClientProcess client;
private boolean continueConnection = true;
private Gradle gradle;
private Server localServer; //this is our server that will listen to the process that started us.
public TaskListClientProtocol(Gradle gradle) {
this.gradle = gradle;
}
/**
* Gives your protocol a chance to store this client so it can access its functions.
*/
public void initialize(ClientProcess client) {
this.client = client;
this.gradle.addListener( new RefreshTaskListBuildListener( client ) );
}
/**
* Listener used to delegate gradle messages to our listeners.
*
*/
private class RefreshTaskListBuildListener extends BuildAdapter implements StandardOutputListener
{
private ClientProcess client;
private StringBuffer allOutputText = new StringBuffer(); //this is potentially threaded, so use StringBuffer instead of StringBuilder
public RefreshTaskListBuildListener(ClientProcess client) {
this.client = client;
}
public synchronized void onOutput(CharSequence output) {
String text = output.toString();
allOutputText.append(text);
}
/**
* <p>Called when the build is completed. All selected tasks have been executed.</p>
*
* @param buildResult The result of the build. Never null.
*/
@Override
public void buildFinished( BuildResult buildResult )
{
boolean wasSuccessful = buildResult.getFailure() == null;
String output = allOutputText.toString();
if (!wasSuccessful) //if we fail, send the results, otherwise, we'll send the projects.
{
//we can't send the exception itself because it might not be serializable (it can include anything from anywhere inside gradle
//or one of its dependencies). So format it as text.
String details = GradlePluginLord.getGradleExceptionMessage(buildResult.getFailure(), gradle.getStartParameter().getShowStacktrace());
output += details;
client.sendMessage(ProtocolConstants.TASK_LIST_COMPLETED_WITH_ERRORS_TYPE, output, wasSuccessful);
} else {
ProjectConverter buildExecuter = new ProjectConverter();
List<ProjectView> projects = new ArrayList<ProjectView>();
projects.addAll(buildExecuter.convertProjects(buildResult.getGradle().getRootProject()));
client.sendMessage(ProtocolConstants.TASK_LIST_COMPLETED_SUCCESSFULLY_TYPE, output, (Serializable) projects);
}
//tell the server we're going to exit.
client.sendMessage(ProtocolConstants.EXITING, null, null);
client.stop();
}
}
/**
* Notification that we have connected to the server. Do minimum handshaking.
*
* @return true if we should continue the connection, false if not.
*/
public boolean serverConnected(Socket clientSocket) {
MessageObject message = client.readMessage();
if (message == null)
{
return false;
}
if (!ProtocolConstants.HANDSHAKE_TYPE.equalsIgnoreCase(message.getMessageType())) {
logger.error("Incorrect server handshaking.");
return false;
}
localServer = new Server(new KillGradleServerProtocol());
localServer.start();
client.sendMessage(ProtocolConstants.HANDSHAKE_TYPE, ProtocolConstants.HANDSHAKE_CLIENT, localServer.getPort());
return true;
}
/**
* We just keep a flag around for this.
*
* @return true if we should keep the connection alive. False if we should
* stop communicaiton.
*/
public boolean continueConnection() {
return continueConnection;
}
public void shutdown() {
continueConnection = false;
}
}