/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.gradle.plugins.node.internal;
import com.liferay.gradle.plugins.node.internal.util.GradleUtil;
import com.liferay.gradle.util.OSDetector;
import com.liferay.gradle.util.Validator;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.process.ExecSpec;
import org.gradle.util.CollectionUtils;
import org.gradle.util.GUtil;
/**
* @author Andrea Di Giorgi
*/
public class NodeExecutor {
public NodeExecutor(Project project) {
_project = project;
_workingDir = _project.getProjectDir();
}
public NodeExecutor args(Iterable<?> args) {
GUtil.addToCollection(_args, args);
return this;
}
public NodeExecutor args(Object... args) {
return args(Arrays.asList(args));
}
public void execute() throws Exception {
File workingDir = getWorkingDir();
workingDir.mkdirs();
if (isUseGradleExec()) {
_executeGradleExec();
}
else {
_executeProcessBuilder();
}
}
public List<Object> getArgs() {
return _args;
}
public String getCommand() {
return GradleUtil.toString(_command);
}
public File getNodeDir() {
return GradleUtil.toFile(_project, _nodeDir);
}
public File getWorkingDir() {
return GradleUtil.toFile(_project, _workingDir);
}
public boolean isInheritProxy() {
return _inheritProxy;
}
public boolean isUseGradleExec() {
return _useGradleExec;
}
public void setArgs(Iterable<?> args) {
_args.clear();
args(args);
}
public void setArgs(Object... args) {
setArgs(Arrays.asList(args));
}
public void setCommand(Object command) {
_command = command;
}
public void setInheritProxy(boolean inheritProxy) {
_inheritProxy = inheritProxy;
}
public void setNodeDir(Object nodeDir) {
_nodeDir = nodeDir;
}
public void setUseGradleExec(boolean useGradleExec) {
_useGradleExec = useGradleExec;
}
public void setWorkingDir(Object workingDir) {
_workingDir = workingDir;
}
private void _executeGradleExec() {
_project.exec(
new Action<ExecSpec>() {
@Override
public void execute(ExecSpec execSpec) {
execSpec.setCommandLine(_getCommandLine());
execSpec.setEnvironment(
_getEnvironment(execSpec.getEnvironment()));
execSpec.setWorkingDir(getWorkingDir());
}
});
}
private void _executeProcessBuilder() throws Exception {
ProcessBuilder processBuilder = new ProcessBuilder(_getCommandLine());
processBuilder.directory(getWorkingDir());
processBuilder.inheritIO();
_updateEnvironment(processBuilder.environment());
if (_logger.isInfoEnabled()) {
_logger.info(
"Running {} from {}", processBuilder.command(),
processBuilder.directory());
}
Process process = processBuilder.start();
int exitValue = process.waitFor();
if (exitValue != 0) {
throw new IOException(
"Process '" + processBuilder.command() +
"' finished with non-zero exit value " + exitValue);
}
}
private List<String> _getCommandLine() {
List<String> commandLine = new ArrayList<>();
if (OSDetector.isWindows()) {
commandLine.add("cmd");
commandLine.addAll(_getWindowsArgs());
}
else {
commandLine.add(_getExecutable());
commandLine.addAll(GradleUtil.toStringList(getArgs()));
}
return commandLine;
}
private Map<String, String> _getEnvironment(Map<?, ?> environment) {
Map<String, String> newEnvironment = new HashMap<>();
GUtil.addToMap(newEnvironment, environment);
_updateEnvironment(newEnvironment);
return newEnvironment;
}
private String _getExecutable() {
String executable = GradleUtil.toString(_command);
File executableDir = _getExecutableDir();
if (executableDir != null) {
File executableFile = new File(executableDir, executable);
executable = executableFile.getAbsolutePath();
}
return executable;
}
private File _getExecutableDir() {
File nodeDir = getNodeDir();
if (nodeDir == null) {
return null;
}
return new File(nodeDir, "bin");
}
private List<String> _getWindowsArgs() {
List<String> windowsArgs = new ArrayList<>(2);
windowsArgs.add("/c");
StringBuilder sb = new StringBuilder();
sb.append('"');
String executable = _getExecutable();
if (executable.indexOf(File.separatorChar) == -1) {
sb.append(executable);
}
else {
sb.append('"');
sb.append(executable);
sb.append('"');
}
List<String> args = GradleUtil.toStringList(getArgs());
for (String arg : args) {
sb.append(" \"");
if (Validator.isNotNull(arg)) {
sb.append(arg);
}
sb.append('"');
}
sb.append('"');
windowsArgs.add(sb.toString());
return windowsArgs;
}
private void _setNonProxyHosts(Map<String, String> environment) {
if (environment.containsKey(_NO_PROXY_KEY) ||
environment.containsKey(_NO_PROXY_KEY.toUpperCase())) {
if (_logger.isInfoEnabled()) {
_logger.info("Non-proxy hosts are already set");
}
return;
}
Set<String> nonProxyHosts = new LinkedHashSet<>();
String hosts = System.getProperty("http.nonProxyHosts");
if (Validator.isNotNull(hosts)) {
Collections.addAll(nonProxyHosts, hosts.split("\\|"));
}
hosts = System.getProperty("https.nonProxyHosts");
if (Validator.isNotNull(hosts)) {
Collections.addAll(nonProxyHosts, hosts.split("\\|"));
}
if (nonProxyHosts.isEmpty()) {
return;
}
hosts = CollectionUtils.join(",", nonProxyHosts);
environment.put(_NO_PROXY_KEY, hosts);
if (_logger.isInfoEnabled()) {
_logger.info("Non-proxy hosts set to {}", hosts);
}
}
private void _setProxy(Map<String, String> environment, String protocol) {
String key = protocol + "_proxy";
if (environment.containsKey(key) ||
environment.containsKey(key.toUpperCase())) {
if (_logger.isInfoEnabled()) {
_logger.info("{} proxy is already set", protocol.toUpperCase());
}
return;
}
String host = System.getProperty(protocol + ".proxyHost");
String port = System.getProperty(protocol + ".proxyPort");
if (Validator.isNull(host) || Validator.isNull(port)) {
return;
}
StringBuilder sb = new StringBuilder();
sb.append(protocol);
sb.append("://");
String user = System.getProperty(protocol + ".proxyUser");
if (Validator.isNotNull(user)) {
sb.append(user);
String password = System.getProperty(protocol + ".proxyPassword");
if (Validator.isNotNull(password)) {
sb.append(':');
sb.append(password);
sb.append('@');
}
}
sb.append(host);
sb.append(':');
sb.append(port);
String url = sb.toString();
if (_logger.isInfoEnabled()) {
_logger.info("{} proxy set to {}", protocol.toUpperCase(), url);
}
environment.put(key, sb.toString());
}
private void _updateEnvironment(Map<String, String> environment) {
if (isInheritProxy()) {
_setNonProxyHosts(environment);
_setProxy(environment, "http");
_setProxy(environment, "https");
}
File executableDir = _getExecutableDir();
if (executableDir != null) {
for (String pathKey : _PATH_KEYS) {
String path = environment.get(pathKey);
if (Validator.isNull(path)) {
continue;
}
path =
executableDir.getAbsolutePath() + File.pathSeparator + path;
environment.put(pathKey, path);
}
}
}
private static final String _NO_PROXY_KEY = "no_proxy";
private static final String[] _PATH_KEYS = {"Path", "PATH"};
private static final Logger _logger = Logging.getLogger(NodeExecutor.class);
private final List<Object> _args = new ArrayList<>();
private Object _command = "node";
private boolean _inheritProxy = true;
private Object _nodeDir;
private final Project _project;
private boolean _useGradleExec;
private Object _workingDir;
}