blob: 412819d2eb020c786820325d0067a04c7694ee6f [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.clerezza.shell;
import org.apache.felix.scr.annotations.Component
import org.osgi.framework.BundleContext
import org.osgi.framework.BundleEvent
import org.osgi.framework.BundleListener
import org.osgi.service.component.ComponentContext
import org.osgi.framework.Bundle
import java.io.{ File, PrintWriter, Reader, StringWriter, BufferedReader, InputStreamReader, InputStream, Writer, OutputStream }
import java.lang.reflect.InvocationTargetException
import java.net._
import java.security.PrivilegedActionException
import java.security.AccessController
import java.security.PrivilegedAction
import javax.script.ScriptContext
import javax.script.{
ScriptEngineFactory => JavaxEngineFactory,
Compilable,
CompiledScript,
ScriptEngine,
AbstractScriptEngine,
Bindings,
SimpleBindings,
ScriptException
}
import java.util.{ ArrayList, Arrays }
import scala.actors.DaemonActor
import scala.collection.immutable
import scala.tools.nsc._
import scala.tools.nsc.interpreter._
import scala.tools.nsc.io.{ AbstractFile, PlainFile, VirtualDirectory }
import scala.tools.nsc.util._
import scala.tools.nsc.symtab.SymbolLoaders
import scala.tools.nsc.reporters.ConsoleReporter
import scala.tools.nsc.reporters.Reporter
import scala.tools.util.PathResolver
import scala.tools.nsc.util.{ ClassPath, JavaClassPath }
import scala.actors.Actor
import scala.actors.Actor._
import org.apache.clerezza.scala.scripting._
import java.io.File
import org.slf4j.scala.Logging
import scala.tools.nsc.interpreter.JLineReader
import scala.tools.jline.Terminal
import scala.tools.jline.console.completer.CompletionHandler
import scala.tools.jline.console.ConsoleReader
import scala.tools.jline.console.completer.CandidateListCompletionHandler
import java.lang.CharSequence
import scala.tools.jline.TerminalFactory
import scala.tools.jline.UnixTerminal
import scala.tools.jline.console.history.History
class Shell(factory: InterpreterFactory, val inStream: InputStream,
outStream: OutputStream, shellCommands: immutable.Set[ShellCommand], terminalOption: Option[Terminal] = None) extends Logging {
private var bundleContext: BundleContext = null
private var bindings = Set[(String, String, Any)]()
private var imports = Set[String]()
private var terminationListeners = Set[Shell.TerminationListener]();
val terminal = terminalOption match {
case Some(x) => x
case None => TerminalFactory.create
}
val interpreterLoop = new ILoop(new BufferedReader(new InputStreamReader(inStream)), new PrintWriter(outStream, true)) {
override def createInterpreter() {
intp = factory.createInterpreter(out)
intp.beQuietDuring {
for (binding <- bindings) {
intp.bind(binding._1, binding._2, binding._3)
}
for (v <- imports) {
intp.interpret("import " + v)
}
}
}
override val prompt = "zz>"
override def isAsync = false
override lazy val standardCommands: List[LoopCommand] = {
import LoopCommand._
(for (shellCommand <- shellCommands) yield {
new LineCmd(shellCommand.command, "<unknown>", shellCommand.description, (line: String) => {
val (continue, linesToRecord) = shellCommand.execute(line, Shell.this.outStream)
Result(continue, linesToRecord)
})
}).toList ::: List(
cmd("help", "[command]", "print this summary or command-specific help", helpCommand),
historyCommand,
cmd("h?", "<string>", "search the history", searchHistory),
cmd("load", "<path>", "load and interpret a Scala file", loadCommand),
nullary("paste", "enter paste mode: all input up to ctrl-D compiled together", pasteCommand),
nullary("power", "enable power user mode", powerCmd),
nullary("quit", "terminate the console shell (use shutdown to shut down clerezza)", () => Result(false, None)),
nullary("replay", "reset execution and replay all previous commands", replay),
shCommand,
nullary("silent", "disable/enable automatic printing of results", verbosity))
}
/** print a friendly help message */
override def helpCommand(line: String): Result = {
if (line == "") printAdditinalHelp();
super.helpCommand(line);
}
def printAdditinalHelp() = {
out println "This is a scala based console, it supports any Scala expression, as well as the command described below."
out println "To access an OSGi service use $[interface]."
out println ""
out println "Initially the following variables are bound:"
for ((name, boundType, value) <- bindings) {
out println (name+": "+boundType+" = "+value)
}
out println ""
out println "This are the initial imports: "
for (v <- imports) {
out println ("import "+v)
}
out println ""
}
override def process(settings: Settings): Boolean = {
this.settings = settings
createInterpreter()
// sets in to some kind of reader depending on environmental cues
//ignore settings.noCompletion.value)
{
val myIn = new StreamJLineReader(new JLineCompletion(intp), inStream, outStream, terminal)
in = myIn
//are we postinit already?
addThunk(myIn.consoleReader.postInit)
}
loadFiles(settings)
// it is broken on startup; go ahead and exit
if (intp.reporter.hasErrors)
return false
// This is about the illusion of snappiness. We call initialize()
// which spins off a separate thread, then print the prompt and try
// our best to look ready. The interlocking lazy vals tend to
// inter-deadlock, so we break the cycle with a single asynchronous
// message to an actor.
if (isAsync) {
intp initialize initializedCallback()
createAsyncListener() // listens for signal to run postInitialization
}
else {
intp.initializeSynchronous()
postInitialization()
}
printWelcome()
try loop()
catch AbstractOrMissingHandler()
finally closeInterpreter()
true
}
override def printWelcome() {
import Properties._
val welcomeMsg =
"""|Welcome to the Apache Clerezza Console
|Console is based on Scala %s (%s, Java %s).
|Type in expressions to have them evaluated.
|Type :help for more information.""".
stripMargin.format(versionString, javaVmName, javaVersion)
echo(welcomeMsg)
}
}
val console: Actor = new DaemonActor {
def act() {
try {
interpreterLoop.process(Array[String]())
} finally {
for (l <- terminationListeners) {
l.terminated
}
println("console terminated")
}
}
}
def start() {
console.start
}
def stop() {
interpreterLoop.command(":q")
interpreterLoop.closeInterpreter()
}
def bind(name: String, boundType: String, value: Any) {
bindings += ((name, boundType, value))
}
def addImport(importValue: String) {
imports += importValue
}
def addTerminationListener(l: Shell.TerminationListener) {
terminationListeners += l
}
def removeTerminationListener(l: Shell.TerminationListener) {
terminationListeners -= l
}
}
object Shell {
trait TerminationListener {
def terminated: Unit
}
trait Environment {
val componentContext: ComponentContext;
val in: InputStream;
val out: OutputStream;
}
}