org.sonatype.gshell.commands.bsf.ScriptCommand.java Source code

Java tutorial

Introduction

Here is the source code for org.sonatype.gshell.commands.bsf.ScriptCommand.java

Source

/*
 * Copyright (c) 2009-2013 the original author or authors.
 *
 * 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.sonatype.gshell.commands.bsf;

import javax.inject.Inject;
import org.apache.bsf.BSFEngine;
import org.apache.bsf.BSFException;
import org.apache.bsf.BSFManager;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileUtil;
import org.fusesource.jansi.Ansi;
import org.sonatype.gshell.command.Command;
import org.sonatype.gshell.command.support.CommandActionSupport;
import org.sonatype.gshell.command.CommandContext;
import org.sonatype.gshell.command.IO;
import org.sonatype.gshell.console.Console;
import org.sonatype.gshell.console.ConsoleErrorHandler;
import org.sonatype.gshell.console.ConsolePrompt;
import org.sonatype.gshell.console.ConsoleTask;
import org.sonatype.gshell.util.cli2.Argument;
import org.sonatype.gshell.util.cli2.Option;
import org.sonatype.gshell.vfs.FileSystemAccess;

import java.util.concurrent.Callable;

/**
 * Provides generic scripting language integration via <a href="http://http://jakarta.apache.org/bsf">BSF</a>.
 *
 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
 * @since 2.0
 */
@Command(name = "script")
public class ScriptCommand extends CommandActionSupport {
    private final BSFManager manager;

    private final FileSystemAccess fileSystemAccess;

    private String language;

    @Option(name = "l", longName = "language")
    private void setLanguage(final String language) {
        assert language != null;

        if (!BSFManager.isLanguageRegistered(language)) {
            throw new RuntimeException("Language is not registered: " + language); // TODO: i18n
        }

        this.language = language;
    }

    @Option(name = "e", longName = "expression")
    private String expression;

    @Argument
    private String path;

    @Inject
    public ScriptCommand(final BSFManager manager, final FileSystemAccess fileSystemAccess) {
        assert manager != null;
        this.manager = manager;
        assert fileSystemAccess != null;
        this.fileSystemAccess = fileSystemAccess;
    }

    public Object execute(final CommandContext context) throws Exception {
        assert context != null;
        IO io = context.getIo();

        if (expression != null && path != null) {
            io.error("Can only specify an expression or a script file"); // TODO: i18n
            return Result.FAILURE;
        } else if (expression != null) {
            return eval(context);
        } else if (path != null) {
            return exec(context);
        }

        return console(context);
    }

    private String detectLanguage(final FileObject file) throws Exception {
        assert file != null;

        return BSFManager.getLangFromFilename(file.getName().getBaseName());
    }

    private BSFEngine createEngine(final CommandContext context) throws BSFException {
        assert context != null;

        // Bind some stuff into the scripting engine's namespace
        manager.declareBean("context", context, CommandContext.class);

        BSFEngine engine = manager.loadScriptingEngine(language);

        log.debug("Created engine: {}", engine);

        return engine;
    }

    private Object eval(final CommandContext context) throws Exception {
        assert context != null;
        IO io = context.getIo();

        if (language == null) {
            io.error("The scripting language must be configured via --language to evaluate an expression"); // TODO: i18n
            return Result.FAILURE;
        }

        log.debug("Evaluating script ({}): {}", language, expression);

        BSFEngine engine = createEngine(context);

        try {
            return engine.eval("<script.expression>", 1, 1, expression);
        } finally {
            engine.terminate();
        }
    }

    private Object exec(final CommandContext context) throws Exception {
        assert context != null;
        IO io = context.getIo();

        FileObject cwd = fileSystemAccess.getCurrentDirectory(context.getVariables());
        FileObject file = fileSystemAccess.resolveFile(cwd, path);

        if (!file.exists()) {
            io.error("File not found: {}", file.getName()); // TODO: i18n
            return Result.FAILURE;
        } else if (!file.getType().hasContent()) {
            io.error("File has not content: {}", file.getName()); // TODO: i18n
            return Result.FAILURE;
        } else if (!file.isReadable()) {
            io.error("File is not readable: {}", file.getName()); // TODO: i18n
            return Result.FAILURE;
        }

        if (language == null) {
            language = detectLanguage(file);
        }

        BSFEngine engine = createEngine(context);

        byte[] bytes = FileUtil.getContent(file);
        String script = new String(bytes);

        log.info("Evaluating file ({}): {}", language, path); // TODO: i18n

        try {
            return engine.eval(file.getName().getBaseName(), 1, 1, script);
        } finally {
            engine.terminate();
            file.close();
        }
    }

    private Object console(final CommandContext context) throws Exception {
        assert context != null;
        IO io = context.getIo();

        if (language == null) {
            io.error("The scripting language must be configured via --language to run an interactive console"); // TODO: i18n
            return Result.FAILURE;
        }

        log.debug("Starting console ({})...", language);

        final BSFEngine engine = createEngine(context);
        final ResultHolder holder = new ResultHolder();

        Callable<ConsoleTask> taskFactory = new Callable<ConsoleTask>() {
            public ConsoleTask call() throws Exception {
                return new ConsoleTask() {
                    @Override
                    public boolean doExecute(String input) throws Exception {
                        //
                        // TODO: Update the allow the console to handle CTRL-D and have that cause the loop to exit
                        //

                        if (input == null || input.trim().equals("exit") || input.trim().equals("quit")) {
                            return false;
                        } else if (!input.trim().equals("")) {
                            holder.result = engine.eval("<script.console>", 1, 1, input);
                        }

                        return true;
                    }
                };
            }
        };

        Console console = new Console(context.getIo(), taskFactory, null, null);

        console.setErrorHandler(new ConsoleErrorHandler() {
            public boolean handleError(final Throwable error) {
                log.error("Script evaluation failed: " + error, error); // TODO: i18n
                return true;
            }
        });

        console.setPrompt(new ConsolePrompt() {
            public String prompt() {
                return Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(language).reset().a("> ").toString();
            }
        });

        console.run();

        engine.terminate();

        return holder.result;
    }

    private class ResultHolder {
        public Object result;
    }
}