| /* |
| * 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; |
| } |
| } |