Configuration.java
/*
*
* MIT License
*
* Copyright (c) [2016] [Saptarshi Debnath]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.saptarshidebnath.lib.processrunner.configuration;
import com.saptarshidebnath.lib.processrunner.constants.ProcessRunnerConstants;
import com.saptarshidebnath.lib.processrunner.exception.ProcessConfigurationException;
import com.saptarshidebnath.lib.processrunner.process.Runner;
import java.io.File;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link Configuration} object holds the configuration.
*
* <p>The created configuration is used by the {@link Runner}.
*/
public class Configuration {
private static final Logger logger = LoggerFactory.getLogger(Configuration.class);
private final String interpreter;
private final String command;
private final Path workingDir;
private final File masterLogFile;
private final Charset charset;
private final boolean autoDeleteFileOnExit;
private final boolean enableLogStreaming;
/**
* Constructor to set the configuration to be consumed by {@link Runner}.
*
* @param interpreter : sets the {@link String} command interpreter like /bin/bash in unix
* @param command : set the actual {@link String} command to be executed
* @param workingDir : sets the working directory in {@link File} format
* @param masterLogFile : {@link File} where the log data will be stored.
* @param charset : a reference of which {@link Charset} to use while writing the {@link
* Configuration#masterLogFile}
* @param autoDeleteFileOnExit : set the flag to denote if the sysout and the syserror {@link
* File} going to be auto deleted on exit.
* @param enableLogStreaming : enable lor disable log streaming by passing a @{@link Boolean}
* value
*/
Configuration(
final String interpreter,
final String command,
final Path workingDir,
final File masterLogFile,
final Charset charset,
final boolean autoDeleteFileOnExit,
final boolean enableLogStreaming) {
this.interpreter = interpreter.trim();
this.command = command.trim();
this.workingDir = workingDir;
this.autoDeleteFileOnExit = autoDeleteFileOnExit;
this.masterLogFile = masterLogFile;
if (this.autoDeleteFileOnExit) {
this.masterLogFile.deleteOnExit();
}
this.enableLogStreaming = enableLogStreaming;
this.charset = charset;
logger.debug("Process Runner Configuration : {}", this);
}
/**
* Getter for flag if {@link Configuration#getMasterLogFile()} is going to auto deleted or not.
*
* @return a {@link Boolean} value depicting the same.
*/
public boolean getAutoDeleteFileOnExit() {
return this.autoDeleteFileOnExit;
}
/**
* A {@link File} reference for the masterLogFile.
*
* @return a {@link File} reference where master logs need to be considered.
*/
public File getMasterLogFile() {
return this.masterLogFile;
}
/**
* Get the currently configured command interpreter.
*
* @return a {@link String} value.
*/
public String getInterpreter() {
return this.interpreter;
}
/**
* Get the command / process to be executed.
*
* @return a {@link String} value
*/
public String getCommand() {
return this.command;
}
/**
* Get the currently configured current directory.
*
* @return a {@link Path} reference where the current working directory is.
*/
public Path getWorkingDir() {
return this.workingDir;
}
/**
* Returns the charset set for the {@link Configuration#masterLogFile}.
*
* @return a reference of the class {@link Charset} in which the {@link
* Configuration#masterLogFile} will be written.
*/
public Charset getCharset() {
return charset;
}
/**
* Denotes if the master log file is going to be auto deleted when teh JVM exits.
*
* @return a {@link Boolean}
*/
public boolean isAutoDeleteFileOnExit() {
return autoDeleteFileOnExit;
}
/**
* Returns {@link Boolean#TRUE} or @{@link Boolean#FALSE} to denote if log streaming on the run
* time is enabled or not.
*
* @return a {@link Boolean}
*/
public boolean isEnableLogStreaming() {
return enableLogStreaming;
}
@Override
public String toString() {
return "Configuration{"
+ "interpreter='"
+ interpreter
+ '\''
+ ", command='"
+ command
+ '\''
+ ", workingDir="
+ workingDir
+ ", masterLogFile="
+ masterLogFile
+ ", charset="
+ charset
+ ", autoDeleteFileOnExit="
+ autoDeleteFileOnExit
+ ", enableLogStreaming="
+ enableLogStreaming
+ '}';
}
/**
* {@link ConfigBuilder} helps in composing a {@link Configuration}.
*
* <p>The {@link Configuration} can be composed using the {@link ConfigBuilder}.
*/
public static class ConfigBuilder {
private static final Logger logger = LoggerFactory.getLogger(ConfigBuilder.class);
private final String interpreter;
private final String command;
private final List<String> comamndParams;
private Path workingDir;
private File masterLogFile;
private boolean autoDeleteFileOnExit;
private boolean logStreamingEnabled;
private Charset charset;
/**
* The constructor for {@link ConfigBuilder}. The only required parameters are the {@link
* String} interpreter and the {@link String} command.
*
* @param interpreter as {@link String} for example "bash", "cmd.exe /c"
* @param command as {@link String} for example "echo 'I solemnly swear I am up to no good'"
* @throws ProcessConfigurationException is thrown if the interpreter or the command is null or
* empty {@link String}
*/
public ConfigBuilder(String interpreter, String command) throws ProcessConfigurationException {
if (interpreter == null || interpreter.trim().length() == 0) {
throw new ProcessConfigurationException(
"Command RunnerImpl Interpreter is set '"
+ interpreter
+ "'. Need a valid command runner interpreter as /bin/bash in unix");
} else if (null == command || 0 == command.trim().length()) {
throw new ProcessConfigurationException(
"Command is set '" + command + "'. Need a valid command like 'echo Hello World'");
}
logger.trace("Interpreter and command validation passed");
this.interpreter = interpreter;
this.command = command;
this.comamndParams = new ArrayList<>();
this.logStreamingEnabled = false;
}
/**
* Add a {@link String} parameter the the command to be executed.
*
* @param param as {@link String}
* @return the {@link ConfigBuilder}
* @throws ProcessConfigurationException if the parameter is null or an empty String.
*/
public ConfigBuilder setParam(String param) throws ProcessConfigurationException {
if (param == null || param.length() == 0) {
throw new ProcessConfigurationException("Param is either null or empty.");
}
logger.trace("Param parameter passed validation");
comamndParams.add(param);
return this;
}
/**
* Add a {@link List} parameter of type {@link String} to the command to be executed.
*
* @param paramList as {@link List} of type {@link String}. The order of the parameters are
* maintained.
* @return the {@link ConfigBuilder}
* @throws ProcessConfigurationException if the parameter is null or an empty String.
*/
public ConfigBuilder setParamList(List<String> paramList) throws ProcessConfigurationException {
if (paramList == null || paramList.isEmpty()) {
throw new ProcessConfigurationException("Param list is either null or empty.");
}
logger.trace("Param list parameter passed validation");
comamndParams.addAll(paramList);
return this;
}
/**
* Set the current working Directory as {@link Path}.
*
* @param workingDir accepts {@link Path} current working directory
* @return the {@link ConfigBuilder}
* @throws ProcessConfigurationException if the working directory is not a directory or it
* doesn't exist.
*/
public ConfigBuilder setWorkigDir(Path workingDir) throws ProcessConfigurationException {
if (!workingDir.toFile().exists() || !workingDir.toFile().isDirectory()) {
throw new ProcessConfigurationException(
"Command's current directory is set '"
+ workingDir.toAbsolutePath().toString()
+ "'. Either the Directory doesn't exist or is not a directory at all");
}
logger.trace("Working dir parameter passed validation");
this.workingDir = workingDir;
return this;
}
/**
* Set the master log file as {@link File}.
*
* <p>Master log file is a {@link File} where all the logs are stored by default. If the {@link
* Configuration#masterLogFile} is not set, the logs are not store anywhere and are discarded.
*
* @param masterLogFile accepts a {@link File} reference as master log file.
* @param autoDeleteFileOnExit accepts a {@link Boolean} flag to determine if the master log
* file is going to be auto deleted or not on JVM exit.
* @return the {@link ConfigBuilder}
* @throws ProcessConfigurationException is thrown if the {@link File} object passed refers to a
* * directory or is a read only file
*/
public ConfigBuilder setMasterLogFile(File masterLogFile, boolean autoDeleteFileOnExit)
throws ProcessConfigurationException {
return this.setMasterLogFile(
masterLogFile, autoDeleteFileOnExit, ProcessRunnerConstants.UTF_8);
}
/**
* Set the master log file as {@link File} with {@link Boolean} autodelete and {@link Charset}.
*
* <p>Master log file is a {@link File} where all the logs are stored by default. If the {@link
* Configuration#masterLogFile} is not set, the logs are not store anywhere and are discarded.
*
* @param masterLogFile accepts a {@link File} reference as master log file.
* @param autoDeleteFileOnExit accepts a {@link Boolean} flag to determine if the master log
* file is going to be auto deleted or not on JVM exit.
* @param charset a reference of {@link Charset} class to set in which chaset the output file is
* going to be written.
* @return the {@link ConfigBuilder}
* @throws ProcessConfigurationException is thrown if the {@link File} object passed refers to a
* directory or is a read only file
*/
public ConfigBuilder setMasterLogFile(
File masterLogFile, boolean autoDeleteFileOnExit, Charset charset)
throws ProcessConfigurationException {
if (masterLogFile.isDirectory()) {
throw new ProcessConfigurationException(
"Master log file : "
+ masterLogFile.getAbsolutePath()
+ " is a directory. It must be a writable file");
} else if (!masterLogFile.canWrite()) {
throw new ProcessConfigurationException(
"Master log file : "
+ masterLogFile.getAbsolutePath()
+ " is a readonly. It must be a writable file");
}
this.masterLogFile = masterLogFile;
this.autoDeleteFileOnExit = autoDeleteFileOnExit;
this.charset = charset;
return this;
}
/**
* Enable or disable log streaming. Please note that streaming will happen or not depends if the
* SLF4j finding the logging framework binding and the current log level set for that particular
* logging framework. All the log streaming will happen in the level equivalent to {@link
* java.util.logging.Level#INFO}
*
* @param logStreamingEnabled a reference of {@link PrintStream}
* @return the {@link ConfigBuilder}
*/
public ConfigBuilder enableLogStreaming(boolean logStreamingEnabled) {
logger.trace("Setting log streaming as per request");
this.logStreamingEnabled = logStreamingEnabled;
return this;
}
/**
* Builds the {@link Configuration} object and returns it back.
*
* @return a reference to the {@link Configuration} object created.
*/
public Configuration build() {
logger.trace("Building configuration");
String commandWithParam =
new StringJoiner(ProcessRunnerConstants.SPACE_STR)
.add(command)
.add(
comamndParams
.stream()
.collect(Collectors.joining(ProcessRunnerConstants.SPACE_STR)))
.toString();
logger.trace("Command to be executed : {}", commandWithParam);
return new Configuration(
interpreter,
commandWithParam,
workingDir,
masterLogFile,
charset,
autoDeleteFileOnExit,
logStreamingEnabled);
}
}
}