blob: 7d186bfb9de66c1cd73b4fccad422ec70314e726 [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.geronimo.gshell.wisdom.shell;
import jline.Completor;
import jline.History;
import org.apache.geronimo.gshell.application.Application;
import org.apache.geronimo.gshell.command.Variables;
import org.apache.geronimo.gshell.commandline.CommandLineExecutor;
import org.apache.geronimo.gshell.console.Console;
import org.apache.geronimo.gshell.console.Console.ErrorHandler;
import org.apache.geronimo.gshell.console.Console.Prompter;
import org.apache.geronimo.gshell.console.JLineConsole;
import org.apache.geronimo.gshell.console.completer.AggregateCompleter;
import org.apache.geronimo.gshell.io.Closer;
import org.apache.geronimo.gshell.io.IO;
import org.apache.geronimo.gshell.notification.ExitNotification;
import org.apache.geronimo.gshell.registry.CommandResolver;
import org.apache.geronimo.gshell.shell.Shell;
import org.apache.geronimo.gshell.shell.ShellContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
* This is the primary implementation of {@link Shell}.
*
* @version $Rev$ $Date$
*/
public class ShellImpl
implements Shell
{
private final Logger log = LoggerFactory.getLogger(getClass());
private final Application application;
private final CommandLineExecutor executor;
private History history;
private List<Completor> completers;
private ShellContext context;
private Prompter prompter;
private ErrorHandler errorHandler;
private boolean opened;
public ShellImpl(final Application application, final CommandLineExecutor executor) {
assert application != null;
this.application = application;
assert executor != null;
this.executor = executor;
}
private synchronized void ensureOpened() {
if (!opened) {
throw new IllegalStateException("Shell has not been opened or has been closed");
}
}
public synchronized boolean isOpened() {
return true;
}
// @PostConstruct
public synchronized void init() throws Exception {
if (opened) {
throw new IllegalStateException("Shell is already opened");
}
log.debug("Initializing");
assert application != null;
// Each shell gets its own variables, using application variables for defaults
final Variables vars = new Variables(application.getVariables());
context = new ShellContext()
{
public Shell getShell() {
return ShellImpl.this;
}
public IO getIo() {
// Shells inherit the application's IO
return application.getIo();
}
public Variables getVariables() {
return vars;
}
};
vars.set("gshell.prompt", application.getModel().getBranding().getPrompt());
vars.set(CommandResolver.GROUP, "/");
vars.set("gshell.username", application.getUserName());
vars.set("gshell.hostname", application.getLocalHost());
// HACK: Add history for the 'history' command, since its not part of the Shell intf it can't really access it
vars.set("gshell.internal.history", getHistory(), true);
loadProfileScripts();
opened = true;
}
public synchronized void close() {
log.debug("Closing");
opened = false;
}
public ShellContext getContext() {
ensureOpened();
if (context == null) {
throw new IllegalStateException("Shell context has not been initialized");
}
return context;
}
public void setCompleters(final List<Completor> completers) {
assert completers != null;
this.completers = completers;
}
public History getHistory() {
if (history == null) {
throw new IllegalStateException("Missing configuration property: history");
}
return history;
}
public void setHistory(final History history) {
this.history = history;
}
public boolean isInteractive() {
return true;
}
public Object execute(final String line) throws Exception {
ensureOpened();
assert executor != null;
return executor.execute(getContext(), line);
}
public Object execute(final String command, final Object[] args) throws Exception {
ensureOpened();
assert executor != null;
return executor.execute(getContext(), command, args);
}
public Object execute(final Object... args) throws Exception {
ensureOpened();
assert executor != null;
return executor.execute(getContext(), args);
}
public void run(final Object... args) throws Exception {
assert args != null;
ensureOpened();
log.debug("Starting interactive console; args: {}", args);
loadUserScript(application.getModel().getBranding().getInteractiveScriptName());
// Setup 2 final refs to allow our executor to pass stuff back to us
final AtomicReference<ExitNotification> exitNotifHolder = new AtomicReference<ExitNotification>();
final AtomicReference<Object> lastResultHolder = new AtomicReference<Object>();
// Whip up a tiny console executor that will execute shell command-lines
Console.Executor executor = new Console.Executor() {
public Result execute(final String line) throws Exception {
assert line != null;
try {
Object result = ShellImpl.this.execute(line);
lastResultHolder.set(result);
}
catch (ExitNotification n) {
exitNotifHolder.set(n);
return Result.STOP;
}
return Result.CONTINUE;
}
};
IO io = getContext().getIo();
// Setup the console runner
JLineConsole console = new JLineConsole(executor, io);
console.setPrompter(getPrompter());
console.setErrorHandler(getErrorHandler());
console.setHistory(getHistory());
// Attach completers if there are any
if (completers != null) {
// Have to use aggregate here to get the completion list to update properly
console.addCompleter(new AggregateCompleter(completers));
}
// Unless the user wants us to shut up, then display a nice welcome banner
if (!io.isQuiet()) {
String message = application.getModel().getBranding().getWelcomeMessage();
if (message != null) {
io.out.print(message);
io.out.println(repeat("-", io.getTerminal().getTerminalWidth() - 1));
io.out.flush();
}
}
// Check if there are args, and run them and then enter interactive
if (args.length != 0) {
execute(args);
}
// And then spin up the console and go for a jog
console.run();
// If any exit notification occured while running, then puke it up
ExitNotification n = exitNotifHolder.get();
if (n != null) {
throw n;
}
}
private static String repeat(final String str, final int repeat) {
StringBuilder buffer = new StringBuilder(repeat * str.length());
for (int i = 0; i < repeat; i++) {
buffer.append(str);
}
return buffer.toString();
}
public Prompter getPrompter() {
if (prompter == null) {
throw new IllegalStateException("Missing configuration property: prompter");
}
return prompter;
}
public void setPrompter(final Prompter prompter) {
this.prompter = prompter;
}
public ErrorHandler getErrorHandler() {
if (errorHandler == null) {
throw new IllegalStateException("Missing configuration property: errorHandler");
}
return errorHandler;
}
public void setErrorHandler(final ErrorHandler handler) {
this.errorHandler = handler;
}
//
// Script Processing
//
private void loadProfileScripts() throws Exception {
log.debug("Loading profile scripts");
// Load profile scripts if they exist
loadSharedScript(application.getModel().getBranding().getProfileScriptName());
loadUserScript(application.getModel().getBranding().getProfileScriptName());
}
private void loadScript(final File file) throws Exception {
assert file != null;
BufferedReader reader = new BufferedReader(new FileReader(file));
try {
String line;
while ((line = reader.readLine()) != null) {
execute(line);
}
}
finally {
Closer.close(reader);
}
}
private void loadUserScript(final String fileName) throws Exception {
assert fileName != null;
File file = new File(application.getModel().getBranding().getUserDirectory(), fileName);
if (file.exists()) {
log.debug("Loading user-script: {}", file);
loadScript(file);
}
else {
log.debug("User script is not present: {}", file);
}
}
private void loadSharedScript(final String fileName) throws Exception {
assert fileName != null;
File file = new File(application.getModel().getBranding().getUserDirectory(), fileName);
if (file.exists()) {
log.debug("Loading shared-script: {}", file);
loadScript(file);
}
else {
log.debug("Shared script is not present: {}", file);
}
}
}