/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.falcon.cli;
import com.sun.jersey.api.client.ClientHandlerException;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.falcon.client.FalconCLIConstants;
import org.apache.falcon.cliParser.CLIParser;
import org.apache.falcon.client.FalconCLIException;
import org.apache.falcon.client.FalconClient;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import static org.apache.falcon.client.FalconCLIConstants.FALCON_URL;
/**
* Falcon Command Line Interface - wraps the RESTful API.
*/
public class FalconCLI {
public static final AtomicReference<PrintStream> ERR = new AtomicReference<PrintStream>(System.err);
public static final AtomicReference<PrintStream> OUT = new AtomicReference<PrintStream>(System.out);
private final Properties clientProperties;
public FalconCLI() throws Exception {
clientProperties = getClientProperties();
}
/**
* Entry point for the Falcon CLI when invoked from the command line. Upon
* completion this method exits the JVM with '0' (success) or '-1'
* (failure).
*
* @param args options and arguments for the Falcon CLI.
*/
public static void main(final String[] args) throws Exception {
System.exit(new FalconCLI().run(args));
}
// TODO help and headers
private static final String[] FALCON_HELP = { "the env variable '" + FALCON_URL
+ "' is used as default value for the '-"
+ FalconCLIConstants.URL_OPTION + "' option",
"custom headers for Falcon web services can be specified using '-D"
+ FalconClient.WS_HEADER_PREFIX + "NAME=VALUE'", };
/**
* Run a CLI programmatically.
* <p/>
* It does not exit the JVM.
* <p/>
* A CLI instance can be used only once.
*
* @param args options and arguments for the Oozie CLI.
* @return '0' (success), '-1' (failure).
*/
public synchronized int run(final String[] args) throws Exception {
CLIParser parser = new CLIParser("falcon", FALCON_HELP);
FalconAdminCLI adminCLI = new FalconAdminCLI();
FalconEntityCLI entityCLI = new FalconEntityCLI();
FalconInstanceCLI instanceCLI = new FalconInstanceCLI();
FalconMetadataCLI metadataCLI = new FalconMetadataCLI();
FalconExtensionCLI extensionCLI = new FalconExtensionCLI();
parser.addCommand(FalconCLIConstants.ADMIN_CMD, "", "admin operations", adminCLI.createAdminOptions(), true);
parser.addCommand(FalconCLIConstants.HELP_CMD, "", "display usage", new Options(), false);
parser.addCommand(FalconCLIConstants.ENTITY_CMD, "",
"Entity operations like submit, suspend, resume, delete, status, definition, submitAndSchedule",
entityCLI.createEntityOptions(), false);
parser.addCommand(FalconCLIConstants.INSTANCE_CMD, "",
"Process instances operations like running, status, kill, suspend, resume, rerun, logs, search",
instanceCLI.createInstanceOptions(), false);
parser.addCommand(FalconCLIConstants.METADATA_CMD, "", "Metadata operations like list, relations",
metadataCLI.createMetadataOptions(), true);
parser.addCommand(FalconCLIConstants.EXTENSION_CMD, "",
"Extension operations like enumerate, definition, describe, list, instances, "
+ "submit, submitAndSchedule, schedule, suspend, resume, delete, update, validate,unregister"
+ ",detail,register",
extensionCLI.createExtensionOptions(), true);
parser.addCommand(FalconCLIConstants.VERSION_OPT, "", "show client version", new Options(), false);
try {
CLIParser.Command command = parser.parse(args);
int exitValue = 0;
if (command.getName().equals(FalconCLIConstants.HELP_CMD)) {
parser.showHelp();
} else {
CommandLine commandLine = command.getCommandLine();
String falconUrl = getFalconEndpoint(commandLine);
FalconClient client = new FalconClient(falconUrl, clientProperties);
setDebugMode(client, commandLine.hasOption(FalconCLIConstants.DEBUG_OPTION));
if (command.getName().equals(FalconCLIConstants.ADMIN_CMD)) {
exitValue = adminCLI.adminCommand(commandLine, client, falconUrl);
} else if (command.getName().equals(FalconCLIConstants.ENTITY_CMD)) {
entityCLI.entityCommand(commandLine, client);
} else if (command.getName().equals(FalconCLIConstants.INSTANCE_CMD)) {
instanceCLI.instanceCommand(commandLine, client);
} else if (command.getName().equals(FalconCLIConstants.METADATA_CMD)) {
metadataCLI.metadataCommand(commandLine, client);
} else if (command.getName().equals(FalconCLIConstants.EXTENSION_CMD)) {
extensionCLI.extensionCommand(commandLine, client);
}
}
return exitValue;
} catch (ParseException ex) {
ERR.get().println("Invalid sub-command: " + ex.getMessage());
ERR.get().println();
ERR.get().println(parser.shortHelp());
ERR.get().println("Stacktrace:");
ex.printStackTrace();
return -1;
} catch (ClientHandlerException ex) {
ERR.get().print("Unable to connect to Falcon server, "
+ "please check if the URL is correct and Falcon server is up and running\n");
ERR.get().println("Stacktrace:");
ex.printStackTrace();
return -1;
} catch (FalconCLIException e) {
ERR.get().println("ERROR: " + e.getMessage());
return -1;
} catch (Exception ex) {
ERR.get().println("Stacktrace:");
ex.printStackTrace();
return -1;
}
}
protected Integer parseIntegerInput(String optionValue, Integer defaultVal, String optionName) {
Integer integer = defaultVal;
if (optionValue != null) {
try {
return Integer.parseInt(optionValue);
} catch (NumberFormatException e) {
throw new FalconCLIException("Input value provided for queryParam \""+ optionName
+"\" is not a valid Integer");
}
}
return integer;
}
protected String getColo(String colo) throws IOException {
if (colo == null) {
Properties prop = getClientProperties();
colo = prop.getProperty(FalconCLIConstants.CURRENT_COLO, "*");
}
return colo;
}
protected String getFalconEndpoint(CommandLine commandLine) throws IOException {
String url = commandLine.getOptionValue(FalconCLIConstants.URL_OPTION);
if (url == null) {
url = System.getenv(FALCON_URL);
}
if (url == null) {
if (clientProperties.containsKey("falcon.url")) {
url = clientProperties.getProperty("falcon.url");
}
}
if (url == null) {
throw new FalconCLIException("Failed to get falcon url from cmdline, or environment or client properties");
}
return url;
}
private void setDebugMode(FalconClient client, boolean debugOpt) {
String debug = System.getenv(FalconCLIConstants.ENV_FALCON_DEBUG);
if (debugOpt) { // CLI argument "-debug" used
client.setDebugMode(true);
} else if (StringUtils.isNotBlank(debug)) {
System.out.println(FalconCLIConstants.ENV_FALCON_DEBUG + ": " + debug);
if (debug.trim().toLowerCase().equals("true")) {
client.setDebugMode(true);
}
}
}
private Properties getClientProperties() throws IOException {
InputStream inputStream = null;
try {
inputStream = FalconCLI.class.getResourceAsStream(FalconCLIConstants.CLIENT_PROPERTIES);
Properties prop = new Properties();
if (inputStream != null) {
prop.load(inputStream);
}
return prop;
} finally {
IOUtils.closeQuietly(inputStream);
}
}
}