/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation: version 3 of
* the License.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.resourcemanager.utils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.objectweb.proactive.core.config.xml.ProActiveConfigurationParser;
import org.ow2.proactive.resourcemanager.core.properties.PAResourceManagerProperties;
import org.ow2.proactive.utils.PAProperties;
import com.google.common.base.Joiner;
/**
* CommandLineBuilder is an utility class that provide users with the capability to automatise
* the RMNodeStarter command line building. We encourage Infrastructure Manager providers to
* use this class as it is used as central point for applying changes to the RMNodeStarter
* properties, for instance, if the classpath needs to be updated, a call to
* will reflect the change.
*
*/
public final class CommandLineBuilder implements Cloneable {
public static final String OBFUSC = "[OBFUSCATED_CRED]";
private static final String ADDONS_DIR = "addons";
private String nodeName;
private String sourceName;
private String javaPath;
private String rmURL;
private String credentialsFile;
private String credentialsValue;
private String credentialsEnv;
private String rmHome;
private int nbNodes = 1;
private Properties paPropProperties;
private List<String> paPropList;
private OperatingSystem targetOS = OperatingSystem.UNIX;
/**
* To get the RMHome from a previous call to the method {@link #setRmHome(String)}. If such a call has not been made,
* one manages to retrieve it from the PAProperties set thanks to a previous call to the method {@link #setPaProperties(java.io.File)} of {@link #setPaProperties(java.util.Map)}.
* @return the RMHome which will be used to build the command line.
*/
public String getRmHome() {
if (this.rmHome != null) {
return rmHome;
} else {
if (paPropProperties != null) {
String rmHome;
if (paPropProperties.getProperty(PAResourceManagerProperties.RM_HOME.getKey()) != null &&
!paPropProperties.getProperty(PAResourceManagerProperties.RM_HOME.getKey()).equals("")) {
rmHome = paPropProperties.getProperty(PAResourceManagerProperties.RM_HOME.getKey());
if (!rmHome.endsWith(String.valueOf(this.targetOS.fs))) {
rmHome += String.valueOf(this.targetOS.fs);
}
} else {
if (PAResourceManagerProperties.RM_HOME.isSet()) {
rmHome = PAResourceManagerProperties.RM_HOME.getValueAsString();
if (!rmHome.endsWith(String.valueOf(this.targetOS.fs))) {
rmHome += String.valueOf(this.targetOS.fs);
}
} else {
RMNodeStarter.logger.warn("No RM Home property found in the supplied configuration. You have to launch RMNodeStarter at the root of the RM Home by yourself.");
rmHome = "";
}
}
return rmHome;
}
}
return null;
}
public void setRmHome(String rmHome) {
this.rmHome = rmHome;
}
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
public void setSourceName(String sourceName) {
this.sourceName = sourceName;
}
public void setJavaPath(String javaPath) {
this.javaPath = javaPath;
}
public void setTargetOS(OperatingSystem targetOS) {
this.targetOS = targetOS;
}
public void setRmURL(String rmURL) {
this.rmURL = rmURL;
}
public void setNumberOfNodes(int nbNodes) {
this.nbNodes = nbNodes;
}
@Deprecated
public void setPaProperties(String paProp) {
this.setPaProperties(Arrays.asList(paProp.split(" ")));
}
public void setPaProperties(List<String> paPropList) {
if (this.paPropProperties != null) {
this.paPropProperties = null;
}
this.paPropList = paPropList;
}
public void setPaProperties(File paPropertiesFile) throws IOException {
this.paPropProperties = new Properties();
if (paPropertiesFile != null) {
if (paPropertiesFile.exists() && paPropertiesFile.isFile()) {
this.paPropProperties = ProActiveConfigurationParser.parse(paPropertiesFile.getAbsolutePath(),
paPropProperties);
} else {
throw new IOException("The supplied file is not a regular file: " + paPropertiesFile.getAbsolutePath());
}
}
}
public void setPaProperties(Map<String, String> paProp) {
this.paPropProperties = new Properties();
for (String key : paProp.keySet()) {
this.paPropProperties.put(key, paProp.get(key));
}
}
/**
* To retrieve the credentials file. If no such call has already been made, will try to retrieve the credentials file path
* from the PAProperties set thanks to the methods: {@link #setPaProperties(java.io.File)} of {@link #setPaProperties(java.util.Map)}
* @return The credentials file used to build the command line
*/
public String getCredentialsFile() {
if (this.credentialsFile != null) {
RMNodeStarter.logger.trace("Credentials file retrieved from previously set value.");
return credentialsFile;
} else {
if (this.credentialsEnv == null && this.credentialsValue == null) {
String paRMKey = PAResourceManagerProperties.RM_CREDS.getKey();
if (paPropProperties != null && paPropProperties.getProperty(paRMKey) != null &&
!paPropProperties.getProperty(paRMKey).equals("")) {
RMNodeStarter.logger.trace(paRMKey + " property retrieved from PA properties supplied by " +
CommandLineBuilder.class.getName());
return paPropProperties.getProperty(paRMKey);
} else {
if (PAResourceManagerProperties.RM_CREDS.isSet()) {
RMNodeStarter.logger.trace(paRMKey +
" property retrieved from PA Properties of parent Resource Manager");
return PAResourceManagerProperties.RM_CREDS.getValueAsString();
}
}
}
}
return credentialsFile;
}
/**
* @return the value of the credentials used to connect as a string
*/
public String getCredentialsValue() {
return credentialsValue;
}
/**
* Sets the credentials value field to the supplied parameter and set
* the other field related to credentials setup to null;
*/
public void setCredentialsValueAndNullOthers(String credentialsValue) {
this.credentialsValue = credentialsValue;
this.credentialsEnv = null;
this.credentialsFile = null;
}
/**
* @return the name of the node
*/
public String getNodeName() {
return nodeName;
}
/**
* @return the name of the node source to which one the node will be added
*/
public String getSourceName() {
return sourceName;
}
/**
* Build the command to launch the RMNode.
* The required pieces of information that need to be set in order to allow the RMNode to start properly are:
* <ul><li>{@link CommandLineBuilder#rmURL}</li><li>{@link CommandLineBuilder#nodeName}</li>
* <li>one of {@link CommandLineBuilder#credentialsEnv}, {@link CommandLineBuilder#credentialsFile} or {@link CommandLineBuilder#credentialsValue}</li></ul>
* @param displayCredentials if true displays the credentials in the command line if false, obfuscates them
* @return The RMNodeStarter command line.
* @throws java.io.IOException if you supplied a ProActive Configuration file that doesn't exist.
*/
public String buildCommandLine(boolean displayCredentials) throws IOException {
List<String> command = this.buildCommandLineAsList(displayCredentials);
return Joiner.on(' ').join(command);
}
/**
* Same as {@link CommandLineBuilder#buildCommandLine(boolean)} but the command is a list of String.
* @param displayCredentials if true displays the credentials in the command line if false, obfuscates them
* @return The RMNodeStarter command line as a list of String.
* @throws java.io.IOException if you supplied a ProActive Configuration file that doesn't exist.
*/
public List<String> buildCommandLineAsList(boolean displayCredentials) throws IOException {
final ArrayList<String> command = new ArrayList<>();
final OperatingSystem os = targetOS;
final Properties paProp = paPropProperties;
String rmHome = this.getRmHome();
if (rmHome != null) {
if (!rmHome.endsWith(os.fs)) {
rmHome = rmHome + os.fs;
}
} else {
rmHome = "";
}
final String libRoot = rmHome + "dist" + os.fs + "lib" + os.fs;
String javaPath = this.javaPath;
if (javaPath != null) {
command.add(javaPath);
} else {
RMNodeStarter.logger.warn("Java path isn't set in RMNodeStarter configuration.");
command.add("java");
}
//building configuration
if (paProp != null) {
Set<Object> keys = paProp.keySet();
for (Object key : keys) {
command.add("-D" + key + "=" + paProp.get(key));
}
} else {
if (this.paPropList != null) {
command.addAll(this.paPropList);
}
}
// forward current charset to the forked JVM
String currentJvmCharset = PAProperties.getFileEncoding();
command.add("-Dfile.encoding=" + currentJvmCharset);
RMNodeStarter.logger.info("Using '" + currentJvmCharset + "' as file encoding");
//building classpath
command.add("-cp");
final StringBuilder classpath = new StringBuilder(".");
// add the content of addons dir on the classpath
classpath.append(os.ps).append(rmHome).append(ADDONS_DIR);
// add jars inside the addons directory
classpath.append(os.ps).append(rmHome).append(ADDONS_DIR).append(os.fs).append("*");
classpath.append(os.ps).append(libRoot).append("*");
command.add(classpath.toString());
command.add(RMNodeStarter.class.getName());
//appending options
String credsEnv = credentialsEnv;
if (credsEnv != null) {
command.add("-" + RMNodeStarter.OPTION_CREDENTIAL_ENV);
command.add(credsEnv);
}
String credsFile = this.getCredentialsFile();
if (credsFile != null) {
command.add("-" + RMNodeStarter.OPTION_CREDENTIAL_FILE);
command.add(credsFile);
}
String credsValue = this.getCredentialsValue();
if (credsValue != null) {
command.add("-" + RMNodeStarter.OPTION_CREDENTIAL_VAL);
command.add(displayCredentials ? credsValue : OBFUSC);
}
String nodename = this.getNodeName();
if (nodename != null) {
command.add("-" + RMNodeStarter.OPTION_NODE_NAME);
command.add(nodename);
}
String nodesource = this.getSourceName();
if (nodesource != null) {
command.add("-" + RMNodeStarter.OPTION_SOURCE_NAME);
command.add(nodesource);
}
String rmurl = rmURL;
if (rmurl != null) {
command.add("-" + RMNodeStarter.OPTION_RM_URL);
command.add(rmurl);
}
command.add("-" + RMNodeStarter.OPTION_WORKERS);
command.add("" + nbNodes);
return command;
}
@Override
public String toString() {
try {
return buildCommandLine(false);
} catch (IOException e) {
return CommandLineBuilder.class.getName() + " with invalid configuration";
}
}
}