/* * Licensed to Luca Cavanna (the "Author") under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. Elastic Search 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.elasticsearch.shell; import java.io.PrintStream; import java.util.concurrent.atomic.AtomicBoolean; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.shell.client.ClientFactory; import org.elasticsearch.shell.command.ScriptLoader; import org.elasticsearch.shell.console.Console; import org.elasticsearch.shell.http.ShellHttpClient; import org.elasticsearch.shell.json.ToXContentAsString; import org.elasticsearch.shell.node.Node; import org.elasticsearch.shell.node.NodeFactory; import org.elasticsearch.shell.scheduler.Scheduler; import org.elasticsearch.shell.script.ScriptExecutor; import org.elasticsearch.shell.source.CompilableSource; import org.elasticsearch.shell.source.CompilableSourceReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Basic shell implementation: it reads a compilable source and executes it * * @author Luca Cavanna */ public class BasicShell<ShellNativeClient, JsonInput, JsonOutput> implements Shell { private static final Logger logger = LoggerFactory.getLogger(BasicShell.class); protected final Console<PrintStream> console; protected final CompilableSourceReader compilableSourceReader; protected final ScriptExecutor scriptExecutor; protected final Unwrapper unwrapper; protected final ShellScope<?> shellScope; protected final ClientFactory<ShellNativeClient> clientFactory; protected final NodeFactory<ShellNativeClient, JsonInput, JsonOutput> nodeFactory; protected final ScriptLoader scriptLoader; protected final Scheduler scheduler; protected final ShellSettings shellSettings; protected final ToXContentAsString toXContentAsString; protected final ShellHttpClient shellHttpClient; protected final AtomicBoolean closed = new AtomicBoolean(false); /** * Creates a new <code>BasicShell</code> * * @param console the console used to read commands and prints the results * @param compilableSourceReader reader that receives a potential script and determines whether * it really is a compilable script or eventually waits for more input * @param scriptExecutor executor used to execute a compilable source * @param unwrapper unwraps a script object and converts it to its Java representation * @param shellScope the generic shell scope * @param scheduler the scheduler that handles all the scheduled actions */ public BasicShell(Console<PrintStream> console, CompilableSourceReader compilableSourceReader, ScriptExecutor scriptExecutor, Unwrapper unwrapper, ShellScope<?> shellScope, ClientFactory<ShellNativeClient> clientFactory, NodeFactory<ShellNativeClient, JsonInput, JsonOutput> nodeFactory, ScriptLoader scriptLoader, Scheduler scheduler, ShellSettings shellSettings, ToXContentAsString toXContentAsString, ShellHttpClient shellHttpClient) { this.console = console; this.compilableSourceReader = compilableSourceReader; this.scriptExecutor = scriptExecutor; this.unwrapper = unwrapper; this.shellScope = shellScope; this.clientFactory = clientFactory; this.nodeFactory = nodeFactory; this.scriptLoader = scriptLoader; this.scheduler = scheduler; this.shellSettings = shellSettings; this.toXContentAsString = toXContentAsString; this.shellHttpClient = shellHttpClient; } @Override public void run() { init(); printLogoAndWelcomeMessage(); loadStartupScript(); if (shellSettings.settings().getAsBoolean(ShellSettings.PLAYGROUND_MODE, false)) { enablePlaygroundMode(); } else { tryRegisterDefaultClient(); } try { doRun(); } finally { close(); } } protected void doRun() { while (true) { CompilableSource source = null; try { source = compilableSourceReader.read(getPrompt()); } catch (Exception e) { logger.error(e.getMessage(), e); console.println("Error while checking the input: " + e.toString()); } if (source != null) { Object jsResult = scriptExecutor.execute(source); Object javaResult = unwrap(jsResult); if (javaResult instanceof ExitSignal) { shutdown(); return; } if (javaResult != null) { console.println(javaToString(javaResult)); } } } } protected String getPrompt() { return MessagesProvider.getMessage(ShellSettings.PROMPT_MESSAGE); } /** * Initializes the shell: nothing to do here but can be overridden */ void init() { } /** * Loads the optional startup script */ void loadStartupScript() { final String startupScript = shellSettings.settings().get(ShellSettings.STARTUP_SCRIPT); if (startupScript != null) { console.print("Loading startup script " + startupScript); new ExecutorWithProgress<Void>(console, new ExecutorWithProgress.ActionCallback<Void>() { @Override public Void execute() { try { scriptLoader.loadScript(startupScript); } catch(Throwable t) { logger.error("Error loading the startup script [{}]", startupScript, t); } return null; } }).execute(); } } /** * Close the shell: nothing to do here but can be overridden */ void close() { } protected void printLogoAndWelcomeMessage() { StringBuilder logoBuilder = new StringBuilder(); logoBuilder.append(" ___ \n"); logoBuilder.append(" // \\\\ \n"); logoBuilder.append(" (( (( )) ").append(MessagesProvider.getMessage(ShellSettings.WELCOME_MESSAGE)); logoBuilder.append(" (( \\\\___// \n"); logoBuilder.append(" {{ // \n"); logoBuilder.append(" (( // )) \n"); logoBuilder.append(" (( // }} \n"); logoBuilder.append(" ((_______)) \n"); console.println(logoBuilder.toString()); } protected void enablePlaygroundMode() { console.print("Enabling playground mode"); final StringBuilder messageBuilder = new StringBuilder(); new ExecutorWithProgress<Void>(console, new ExecutorWithProgress.ActionCallback<Void>() { @Override public Void execute() { Node<ShellNativeClient,JsonInput,JsonOutput> node = nodeFactory.newLocalNode(); shellScope.registerJavaObject("node", node); messageBuilder.append(node.toString()).append(" available as node").append("\n"); ShellNativeClient shellNativeClient = node.client(); shellScope.registerJavaObject("es", shellNativeClient); messageBuilder.append(shellNativeClient.toString()).append(" available as es"); return null; } }).execute(); console.println(messageBuilder.toString()); } protected void tryRegisterDefaultClient() { ShellNativeClient shellNativeClient = clientFactory.newTransportClient(); if (shellNativeClient != null) { registerClient(shellNativeClient); } } protected void registerClient(ShellNativeClient shellNativeClient) { shellScope.registerJavaObject("es", shellNativeClient); console.println(shellNativeClient.toString() + " available as es"); } /** * Converts a javascript object returned by the shell to its Java representation * @param scriptObject the javascript object to convert * @return the Java representation of the input javascript object */ protected Object unwrap(Object scriptObject) { return unwrapper.unwrap(scriptObject, true); } /** * Converts a Java object to its textual representation that can be shown as a result of a script execution * @param javaObject the Java object to convert to its textual representation * @return the textual representation of the input Java object */ protected String javaToString(Object javaObject) { //We wanna show a string output whenever there's a ToXContent, which we can convert to json if (javaObject instanceof ToXContent) { ToXContent toXContent = (ToXContent) javaObject; try { return toXContentAsString.asString(toXContent); } catch (Exception e) { logger.warn("Error while trying to convert a {} to XContent", javaObject.getClass().getSimpleName(), e); } } return javaObject.toString(); } @Override public void shutdown() { if (closed.compareAndSet(false, true)) { if (scheduler != null) { logger.debug("Shutting down the scheduler"); scheduler.shutdown(); } shellScope.closeAllResources(); shellHttpClient.shutdown(); console.println(); console.println(MessagesProvider.getMessage(ShellSettings.BYE_MESSAGE)); console.shutdown(); } } }