/* * Copyright 2015-2016 OpenCB * * 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.opencb.opencga.storage.app.cli; import com.beust.jcommander.JCommander; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.*; import org.opencb.opencga.storage.core.config.StorageConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; /** * Created by imedina on 02/03/15. */ public abstract class CommandExecutor { protected String logLevel; protected String logFile; protected boolean verbose; protected String configFile; protected String appHome; protected String storageEngine; protected StorageConfiguration configuration; protected Logger logger; public CommandExecutor(GeneralCliOptions.CommonOptions options) { init(options); } protected void init(GeneralCliOptions.CommonOptions options) { init(options.logLevel, options.verbose, options.configFile, options.storageEngine); } protected void init(String logLevel, boolean verbose, String configFile, String storageEngine) { this.logLevel = logLevel; this.verbose = verbose; this.configFile = configFile; this.storageEngine = storageEngine; /** * System property 'app.home' is automatically set up in opencga-storage.sh. If by any reason * this is 'null' then OPENCGA_HOME environment variable is used instead. */ this.appHome = System.getProperty("app.home", System.getenv("OPENCGA_HOME")); if (verbose) { logLevel = "debug"; } if (logLevel != null && !logLevel.isEmpty()) { // We must call to this method configureDefaultLog(logLevel); } } public abstract void execute() throws Exception; public String getLogLevel() { return logLevel; } public void configureDefaultLog(String logLevel) { // This small hack allow to configure the appropriate Logger level from the command line, this is done // by setting the DEFAULT_LOG_LEVEL_KEY before the logger object is created. // System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, logLevel); org.apache.log4j.Logger rootLogger = LogManager.getRootLogger(); // rootLogger.setLevel(Level.toLevel(logLevel)); ConsoleAppender stderr = (ConsoleAppender) rootLogger.getAppender("stderr"); stderr.setThreshold(Level.toLevel(logLevel)); logger = LoggerFactory.getLogger(this.getClass().toString()); this.logLevel = logLevel; } public String getLogFile() { return logFile; } public void setLogFile(String logFile) { this.logFile = logFile; } public boolean isVerbose() { return verbose; } public void setVerbose(boolean verbose) { this.verbose = verbose; } public String getConfigFile() { return configFile; } public void setConfigFile(String configFile) { this.configFile = configFile; } public Logger getLogger() { return logger; } /** * This method attempts to first data configuration from CLI parameter, if not present then uses * the configuration from installation directory, if not exists then loads JAR storage-configuration.yml. * * @throws IOException If any IO problem occurs */ public void loadStorageConfiguration() throws IOException { String loadedConfigurationFile; if (this.configFile != null) { loadedConfigurationFile = this.configFile; this.configuration = StorageConfiguration.load(new FileInputStream(new File(this.configFile))); } else { // We load configuration file either from app home folder or from the JAR Path path = Paths.get(appHome + "/conf/storage-configuration.yml"); if (appHome != null && Files.exists(path)) { loadedConfigurationFile = path.toString(); this.configuration = StorageConfiguration.load(new FileInputStream(path.toFile())); } else { loadedConfigurationFile = StorageConfiguration.class.getClassLoader().getResourceAsStream("storage-configuration.yml") .toString(); this.configuration = StorageConfiguration .load(StorageConfiguration.class.getClassLoader().getResourceAsStream("storage-configuration.yml")); } } // logLevel parameter has preference in CLI over configuration file if (this.logLevel == null || this.logLevel.isEmpty()) { this.logLevel = this.configuration.getLogLevel(); configureDefaultLog(this.logLevel); } else { if (!this.logLevel.equalsIgnoreCase(this.configuration.getLogLevel())) { this.configuration.setLogLevel(this.logLevel); configureDefaultLog(this.logLevel); } } // logFile parameter has preference in CLI over configuration file, we first set the logFile passed if (this.logFile != null && !this.logFile.isEmpty()) { this.configuration.setLogFile(logFile); } // If user has set up a logFile we redirect logs to it if (this.configuration.getLogFile() != null && !this.configuration.getLogFile().isEmpty()) { org.apache.log4j.Logger rootLogger = LogManager.getRootLogger(); // If a log file is used then console log is removed rootLogger.removeAppender("stderr"); // Creating a RollingFileAppender to output the log RollingFileAppender rollingFileAppender = new RollingFileAppender(new PatternLayout("%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - " + "%m%n"), this.configuration.getLogFile(), true); rollingFileAppender.setThreshold(Level.toLevel(configuration.getLogLevel())); rootLogger.addAppender(rollingFileAppender); } logger.debug("Loading configuration from '{}'", loadedConfigurationFile); } protected boolean runCommandLineProcess(File workingDirectory, String binPath, List<String> args, String logFilePath) throws IOException, InterruptedException { ProcessBuilder builder = getProcessBuilder(workingDirectory, binPath, args, logFilePath); logger.debug("Executing command: " + StringUtils.join(builder.command(), " ")); Process process = builder.start(); process.waitFor(); // Check process output boolean executedWithoutErrors = true; int genomeInfoExitValue = process.exitValue(); if (genomeInfoExitValue != 0) { logger.warn("Error executing {}, error code: {}. More info in log file: {}", binPath, genomeInfoExitValue, logFilePath); executedWithoutErrors = false; } return executedWithoutErrors; } private ProcessBuilder getProcessBuilder(File workingDirectory, String binPath, List<String> args, String logFilePath) { List<String> commandArgs = new ArrayList<>(); commandArgs.add(binPath); commandArgs.addAll(args); ProcessBuilder builder = new ProcessBuilder(commandArgs); // working directoy and error and output log outputs if (workingDirectory != null) { builder.directory(workingDirectory); } builder.redirectErrorStream(true); if (logFilePath != null) { builder.redirectOutput(ProcessBuilder.Redirect.appendTo(new File(logFilePath))); } return builder; } public StorageConfiguration getConfiguration() { return configuration; } public void setConfiguration(StorageConfiguration configuration) { this.configuration = configuration; } public static String getParsedSubCommand(JCommander jCommander) { String parsedCommand = jCommander.getParsedCommand(); if (jCommander.getCommands().containsKey(parsedCommand)) { String subCommand = jCommander.getCommands().get(parsedCommand).getParsedCommand(); return subCommand != null ? subCommand: ""; } else { return ""; } } }