/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.enterprise.client.commands;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Arrays;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.bindings.ScriptEngineFactory;
import org.rhq.bindings.StandardBindings;
import org.rhq.bindings.client.RhqManager;
import org.rhq.bindings.output.TabularWriter;
import org.rhq.enterprise.client.ClientMain;
import org.rhq.enterprise.client.Controller;
import org.rhq.enterprise.client.proxy.ConfigurationEditor;
import org.rhq.enterprise.client.proxy.EditableResourceClientFactory;
import org.rhq.enterprise.client.script.CLIScriptException;
import org.rhq.enterprise.client.script.CmdLineParser;
import org.rhq.enterprise.client.script.CommandLineParseException;
import org.rhq.enterprise.client.script.NamedScriptArg;
import org.rhq.enterprise.client.script.ScriptArg;
import org.rhq.enterprise.client.script.ScriptCmdLine;
import org.rhq.scripting.ScriptSourceProvider;
import org.rhq.scripting.ScriptSourceProviderFactory;
/**
* @author Greg Hinkle
* @author Lukas Krejci
*/
public class ScriptCommand implements ClientCommand {
private StandardBindings bindings;
private final Log log = LogFactory.getLog(ScriptCommand.class);
private StringBuilder script = new StringBuilder();
private boolean isMultilineScript = false;
private boolean inMultilineScript = false;
public String getPromptCommandString() {
return "exec";
}
public boolean execute(ClientMain client, String[] args) {
// for a command line session we don't want to reset the bindings for each executed command line, the
// state, e.g. exporter settings, should be maintained from line to line. Note that scriptFiles
// executed via 'exec -f' are treated like extensions of the command line session. They inherit the
// current bindings and any modifications made by the script file will affect the command line session
// after the script file has completed.
if (null == bindings) {
initBindings(client);
}
if (isScriptFileCommandLine(args)) {
try {
CmdLineParser cmdLineParser = new CmdLineParser();
ScriptCmdLine scriptCmdLine = cmdLineParser.parse(args);
bindScriptArgs(client, scriptCmdLine);
executeUtilScripts(client);
FileReader reader = new FileReader(scriptCmdLine.getScriptFileName());
try {
return executeScriptFile(reader, client);
} finally {
try {
reader.close();
} catch (IOException ignore) {
}
}
} catch (FileNotFoundException e) {
if (client.isInteractiveMode()) {
client.getPrintWriter().println(e.getMessage());
if (log.isDebugEnabled()) {
log.debug("Unable to locate script file: " + e.getMessage());
}
} else {
throw new CLIScriptException(e);
}
} catch (CommandLineParseException e) {
if (client.isInteractiveMode()) {
client.getPrintWriter().println("parse error: " + e.getMessage());
if (log.isDebugEnabled()) {
log.debug("A parse error occurred.", e);
}
} else {
throw new CLIScriptException(e);
}
}
return true;
}
isMultilineScript = "\\".equals(args[args.length - 1]);
inMultilineScript = inMultilineScript || isMultilineScript;
if (!isMultilineScript && !inMultilineScript) {
script = new StringBuilder();
}
if (isMultilineScript) {
args = Arrays.copyOfRange(args, 0, args.length - 1);
}
for (int i = ("exec".equals(args[0]) ? 1 : 0); i < args.length; i++) {
script.append(args[i]);
script.append(" ");
}
if (isMultilineScript) {
return true;
}
try {
Object result = client.getScriptEngine().eval(script.toString());
inMultilineScript = false;
script = new StringBuilder();
if (result != null) {
// client.getPrintWriter().print("result: ");
TabularWriter writer = new TabularWriter(client.getPrintWriter());
if (client.isInteractiveMode()) {
writer.setWidth(client.getConsoleWidth());
}
writer.print(result);
}
} catch (ScriptException e) {
if (client.isInteractiveMode()) {
String message = client.getUsefulErrorMessage(e);
client.getPrintWriter().println(message);
client.getPrintWriter().println(script);
for (int i = 0; i < e.getColumnNumber(); i++) {
client.getPrintWriter().print(" ");
}
client.getPrintWriter().println("^");
script = new StringBuilder();
inMultilineScript = false;
} else {
throw new CLIScriptException(e);
}
}
client.getPrintWriter().println();
return true;
}
private void initBindings(ClientMain client) {
bindings = new StandardBindings(client.getPrintWriter(), client.getRemoteClient());
// init pretty width with console setting
bindings.getPretty().getValue().setWidth(client.getConsoleWidth());
// replace ResourceClientFactory with Editable version or none at all
if (client.getRemoteClient() != null) {
bindings.getProxyFactory().setValue(new EditableResourceClientFactory(client));
} else {
bindings.getProxyFactory().setValue(null);
}
//non-standard bindings for console
bindings.put("configurationEditor", new ConfigurationEditor(client));
bindings.put("rhq", new Controller(client));
ScriptEngine engine = client.getScriptEngine();
ScriptSourceProvider[] sourceProviders = ScriptSourceProviderFactory.get(null);
ScriptEngineFactory.injectStandardBindings(engine, bindings, false, sourceProviders);
ScriptEngineFactory.bindIndirectionMethods(engine, "configurationEditor");
ScriptEngineFactory.bindIndirectionMethods(engine, "rhq");
}
public void initClient(ClientMain client) {
if (null == bindings) {
initBindings(client);
} else {
ScriptEngine engine = client.getScriptEngine();
// remove any current manager bindings from the engine, they may not be valid for the
// new client. The new standard bindings will include any new managers.
ScriptEngineFactory.removeBindings(engine, bindings.getManagers().keySet());
bindings.setFacade(client.getPrintWriter(), client.getRemoteClient());
// update the engine with the new client bindings. Keep the existing engine bindings as they
// may contain bindings outside this standard set (like any var created by the script or command line user)
ScriptEngineFactory.injectStandardBindings(engine, bindings, false, ScriptSourceProviderFactory.get(null));
}
return;
}
private void executeUtilScripts(ClientMain client) {
InputStream stream = getClass().getResourceAsStream("test_utils.js");
InputStreamReader reader = new InputStreamReader(stream);
try {
client.getScriptEngine().eval(reader);
} catch (ScriptException e) {
log.warn("An error occurred while executing test_utils.js", e);
}
}
private boolean isScriptFileCommandLine(String[] args) {
if (args == null || args.length < 3) {
return false;
}
for (int i = 0; i < args.length; ++i) {
if (args[i].equals("-f")) {
return true;
}
}
return false;
}
private void bindScriptArgs(ClientMain client, ScriptCmdLine cmdLine) {
bindArgsArray(client, cmdLine);
if (cmdLine.getArgType() == ScriptCmdLine.ArgType.NAMED) {
bindNamedArgs(client, cmdLine);
}
client.getScriptEngine().put("script", new File(cmdLine.getScriptFileName()).getName());
}
private void bindArgsArray(ClientMain client, ScriptCmdLine cmdLine) {
String[] args = new String[cmdLine.getArgs().size()];
int i = 0;
for (ScriptArg arg : cmdLine.getArgs()) {
args[i++] = arg.getValue();
}
client.getScriptEngine().put("args", args);
}
private void bindNamedArgs(ClientMain client, ScriptCmdLine cmdLine) {
for (ScriptArg arg : cmdLine.getArgs()) {
NamedScriptArg namedArg = (NamedScriptArg) arg;
client.getScriptEngine().put(namedArg.getName(), namedArg.getValue());
}
}
private boolean executeScriptFile( Reader reader, ClientMain client) {
try {
Object result = client.getScriptEngine().eval(reader);
if (result != null) {
if (client.isInteractiveMode()) {
new TabularWriter(client.getPrintWriter()).print(result);
}
}
} catch (ScriptException e) {
if (client.isInteractiveMode()) {
client.getPrintWriter().println(e.getMessage());
client.getPrintWriter().println("^");
} else {
throw new CLIScriptException(e);
}
}
return true;
}
public String getSyntax() {
return getPromptCommandString() + " <statement> | [-s<indexed|named>] -f <file> [args]";
}
public String getHelp() {
return "Execute a statement or a script";
}
public String getDetailedHelp() {
return "Execute a statement or a script. The following service managers are available: "
+ Arrays.toString(RhqManager.values());
}
}