blob: 496afd971d0575c8b9ba5055289fbd9dd71daa97 [file] [log] [blame]
/*
* 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.samza.sql.client.cli;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.samza.sql.client.exceptions.CommandHandlerException;
import org.apache.samza.sql.client.interfaces.CommandHandler;
import org.apache.samza.sql.client.interfaces.EnvironmentVariableHandler;
import org.apache.samza.sql.client.interfaces.EnvironmentVariableSpecs;
import org.apache.samza.sql.client.exceptions.ExecutorException;
import org.apache.samza.sql.client.interfaces.SqlExecutor;
import org.apache.samza.sql.client.exceptions.CliException;
import org.apache.samza.sql.client.util.CliUtil;
import org.apache.samza.sql.client.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* CliEnvironment handles "environment variables" that configures the shell behavior.
*/
public class CliEnvironment {
private EnvironmentVariableHandler shellEnvHandler;
private EnvironmentVariableHandler executorEnvHandler;
private SqlExecutor executor;
private List<CommandHandler> commandHandlers;
private Map<String, String> delayedExecutorVars;
// shell.executor is special and is specifically handled by CliEnvironment
private String activeExecutorClassName;
private static final Logger LOG = LoggerFactory.getLogger(CliEnvironment.class);
CliEnvironment() {
shellEnvHandler = new CliShellEnvironmentVariableHandler();
commandHandlers = new ArrayList<>();
}
/** Sets the value of an environment variable.
* @param name Environment variable name
* @param value Value of the environment variable
* @return 0 : succeed
* @throws ExecutorException When user sets an executor but not being able to create
* @throws CommandHandlerException When user sets a CommandHandler but was not created successfully
* -1: invalid name
* -2: invalid value
*/
public int setEnvironmentVariable(String name, String value) throws ExecutorException, CommandHandlerException {
name = name.toLowerCase();
if (name.equals(CliConstants.CONFIG_EXECUTOR)) {
createShellExecutor(value);
activeExecutorClassName = value;
executorEnvHandler = executor.getEnvironmentVariableHandler();
return 0;
}
if (name.equals(CliConstants.CONFIG_COMMAND_HANDLER)) {
List<String> commandHandlersNames = Arrays.asList(value.split(","));
for (String commandHandlerName : commandHandlersNames) {
createCommandHandler(commandHandlerName.trim());
}
return 0;
}
EnvironmentVariableHandler handler = getAppropriateHandler(name);
if (handler == null) {
// Shell doesn't recognize this variable. There's no executor handler yet. Save for future executor
if (delayedExecutorVars == null) {
delayedExecutorVars = new HashMap<>();
}
delayedExecutorVars.put(name, value);
return 0;
}
return handler.setEnvironmentVariable(name, value);
}
/**
* Gets the value of an environment variable.
* @param name environment variable name
* @return value of the environment variable. Returns null if the variable is not set.
*/
public String getEnvironmentVariable(String name) {
if (name.equalsIgnoreCase(CliConstants.CONFIG_EXECUTOR)) {
return activeExecutorClassName;
}
EnvironmentVariableHandler handler = getAppropriateHandler(name);
if (handler == null)
return null;
return handler.getEnvironmentVariable(name);
}
/**
* Gets all possible valid values of an environment variable.
* @param name environment variable name
* @return An array of all possible valid values of the environment variable. Returns null
* if the environment variable name given is invalid.
*/
public String[] getPossibleValues(String name) {
EnvironmentVariableHandler handler = getAppropriateHandler(name);
if (handler == null)
return null;
EnvironmentVariableSpecs.Spec spec = handler.getSpecs().getSpec(name);
return spec == null ? null : spec.getPossibleValues();
}
/**
* Prints all the environment variables and their values, including both those of Shell
* and of the Executor.
* @param writer A writer to print to
*/
public void printAll(PrintWriter writer) {
printVariable(writer, CliConstants.CONFIG_EXECUTOR, activeExecutorClassName);
printAll(writer, shellEnvHandler);
if (executorEnvHandler != null) {
printAll(writer, executorEnvHandler);
}
}
/**
* Gives CliEnvironment a chance to apply settings, especially during initialization, things like
* making default values take effect
*/
public void finishInitialization() throws CliException {
if (executor == null) {
try {
createShellExecutor(CliConstants.DEFAULT_EXECUTOR_CLASS);
activeExecutorClassName = CliConstants.DEFAULT_EXECUTOR_CLASS;
executorEnvHandler = executor.getEnvironmentVariableHandler();
if (delayedExecutorVars != null) {
for (Map.Entry<String, String> entry : delayedExecutorVars.entrySet()) {
setEnvironmentVariable(entry.getKey(), entry.getValue());
}
delayedExecutorVars = null;
}
} catch (ExecutorException | CommandHandlerException e) {
// Convert checked exception ExecutorException to an unchecked exception as
// we have failed to create even the default executor thus not recoverable
throw new CliException(e);
}
}
finishInitialization(shellEnvHandler);
finishInitialization(executorEnvHandler);
}
/*
* Sets any environment variable that has not be set by configuration file or user to the default value
*/
private void finishInitialization(EnvironmentVariableHandler handler) {
List<Pair<String, EnvironmentVariableSpecs.Spec>> list = handler.getSpecs().getAllSpecs();
for (Pair<String, EnvironmentVariableSpecs.Spec> pair : list) {
String name = pair.getL();
EnvironmentVariableSpecs.Spec spec = pair.getR();
if (CliUtil.isNullOrEmpty(handler.getEnvironmentVariable(name))) {
handler.setEnvironmentVariable(name, spec.getDefaultValue());
}
}
}
/**
* retrieves the {@link SqlExecutor} setup in this environment
* @return {@link SqlExecutor}
*/
public SqlExecutor getExecutor() {
return executor;
}
/**
* retrieves the list of {@link CommandHandler}s setup in this environment
* @return List of {@link CommandHandler}
*/
public List<CommandHandler> getCommandHandlers() {
return commandHandlers;
}
private void createShellExecutor(String executorClassName) throws ExecutorException {
try {
executor = (SqlExecutor) createInstance(executorClassName);
} catch (ClassCastException e) {
String errMsg = String.format("Error trying to cast Object of class %s to SqlExecutor", executorClassName);
LOG.error(errMsg);
throw new ExecutorException(errMsg, e);
} catch (Exception e) {
LOG.error(e.getMessage());
throw new ExecutorException(e);
}
}
private void createCommandHandler(String handlerClassName) throws CommandHandlerException {
try {
commandHandlers.add((CommandHandler) createInstance(handlerClassName));
} catch (ClassCastException e) {
String errMsg = String.format("Error trying to cast Object of class %s to CommandHandler", handlerClassName);
LOG.error(errMsg);
throw new CommandHandlerException(errMsg, e);
} catch (Exception e) {
LOG.error(e.getMessage());
throw new CommandHandlerException(e);
}
}
private Object createInstance(String className) throws Exception {
try {
Class<?> clazz = Class.forName(className);
Constructor<?> ctor = clazz.getConstructor();
return ctor.newInstance();
} catch (ClassNotFoundException | NoSuchMethodException
| IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new ExecutorException(e);
}
}
/*
* Gets the corresponding EnvironmentVariableHandler by the environment variable name.
* Names starting CliConstants.CONFIG_SHELL_PREFIX are supposed by the Shell itself, otherwise by
* the executor.
*/
private EnvironmentVariableHandler getAppropriateHandler(String name) {
if (name.startsWith(CliConstants.CONFIG_SHELL_PREFIX)) {
return shellEnvHandler;
} else {
return executorEnvHandler;
}
}
/*
* Helper function for printing all environment variables and their values of a handler to a writer.
*/
private void printAll(PrintWriter writer, EnvironmentVariableHandler handler) {
List<Pair<String, String>> shellEnvs = handler.getAllEnvironmentVariables();
for (Pair<String, String> pair : shellEnvs) {
printVariable(writer, pair.getL(), pair.getR());
}
}
/*
* Prints "name = value\n" to a writer.
*/
public static void printVariable(PrintWriter writer, String name, String value) {
writer.print(name);
writer.print(" = ");
writer.print(value);
writer.print('\n');
}
}