org.gradle.foundation.ipc.basic.ProcessLauncherServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2010 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.basic;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.foundation.common.ObserverLord;
import org.gradle.process.ExecResult;
import org.gradle.process.internal.DefaultExecHandleBuilder;
import org.gradle.process.internal.ExecHandle;
import org.gradle.process.internal.ExecHandleBuilder;
import java.io.ByteArrayOutputStream;
/**
* This launches an application as a separate process then listens for messages from it. You implement the Protocol interface to handle the specifics of the communications. To use this, instantiate
* it, then call start. When the communications are finished, call requestShutdown(). Your server's protocol can call sendMessage once communication is started to respond to client's messages.
*/
public class ProcessLauncherServer extends Server {
private volatile ExecHandle externalProcess;
private static final Logger LOGGER = Logging.getLogger(ProcessLauncherServer.class);
/**
* Implement this to define the behavior of the communication on the server side.
*/
public interface Protocol extends Server.Protocol {
public void aboutToKillProcess();
/**
* Fill in the ExecutionInfo object with information needed to execute the other process.
*
* @param serverPort the port the server is listening on. The client should send messages here
* @return an executionInfo object containing information about what we execute.
*/
public ExecutionInfo getExecutionInfo(int serverPort);
/**
* Notification that the client has shutdown. Note: this can occur before communications has ever started. You SHOULD get this notification before receiving serverExited, even if the client
* fails to launch or locks up.
*
* @param result the return code of the client application
* @param output the standard error and standard output of the client application
*/
public void clientExited(int result, String output);
}
public interface ServerObserver extends Server.ServerObserver {
/**
* Notification that the client has shutdown. Note: this can occur before communications has ever started. You SHOULD get this notification before receiving serverExited, even if the client
* fails to launch or locks up.
*
* @param result the return code of the client application
* @param output the standard error and standard output of the client application
*/
public void clientExited(int result, String output);
}
public ProcessLauncherServer(Protocol protocol) {
super(protocol);
}
@Override
protected void communicationsStarted() {
launchExternalProcess();
}
/**
* This launches an external process in a thread and waits for it to exit.
*/
private void launchExternalProcess() {
Thread thread = new Thread(new Runnable() {
public void run() {
ExecutionInfo executionInfo = null;
ExecHandle execHandle = null;
ByteArrayOutputStream output = null;
try {
executionInfo = protocol.getExecutionInfo(getPort());
ExecHandleBuilder builder = new DefaultExecHandleBuilder();
builder.workingDir(executionInfo.getWorkingDirectory());
builder.commandLine((Object[]) executionInfo.getCommandLineArguments());
builder.environment(executionInfo.getEnvironmentVariables());
output = new ByteArrayOutputStream();
builder.setStandardOutput(output);
builder.setErrorOutput(output);
execHandle = builder.build();
setExternalProcess(execHandle);
execHandle.start();
} catch (Throwable e) {
LOGGER.error("Starting external process", e);
notifyClientExited(-1, e.getMessage());
setExternalProcess(null);
return;
}
ExecResult result = execHandle.waitForFinish();
LOGGER.debug("External process completed with exit code {}", result.getExitValue());
setExternalProcess(null); //clear our external process member variable (we're using our local variable below). This is so we know the process has already stopped.
executionInfo.processExecutionComplete();
notifyClientExited(result.getExitValue(), output.toString());
}
});
thread.start();
}
public void stop() {
super.stop();
killProcess(); //if the process is still running, shut it down
}
public void setExternalProcess(ExecHandle externalProcess) {
this.externalProcess = externalProcess;
}
/**
* Call this to violently kill the external process. This is NOT a good way to stop it. It is preferable to ask the thread to stop. However, gradle has no way to do that, so we'll be killing it.
*/
public synchronized void killProcess() {
if (externalProcess != null) {
requestShutdown();
protocol.aboutToKillProcess();
externalProcess.abort();
setExternalProcess(null);
notifyClientExited(-1, "Process Canceled");
}
}
private void notifyClientExited(final int result, final String output) {
protocol.clientExited(result, output);
observerLord.notifyObservers(new ObserverLord.ObserverNotification() {
public void notify(ServerObserver observer) {
observer.clientExited(result, output);
}
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy