K
Konstantin Denerz
Hallo,
hier ist ein kleines Beispiel, dass zeigen soll, wie einfach man in seine kompilierte Scala-Anwendung, den Scala-Interpreter einbauen kann, um bestimmte Bereiche der Anwendung flexibel zu halten. Die Flexibilität erfolgt über in Scala geschriebene Skripte, die zur Laufzeit dynamisch kompiliert (interpretiert) werden. In dem Beispiel wird auch gezeigt, wie man bestimmte Kontexte an ein Skript bzw. an den Interpreter bindet. Damit lassen sich die Kontexte durch Skripte abrufen und manipulieren.
Umgebung:
Eclipse 3.4
Scala Plugin Beta (nightly build)
Scala 2.7.1 Final (scala-library.jar, scala-compiler.jar)
Beispiel:
In dem Beispiel erstelle ich einen Kontext(UserContext), der bestimmte Informationen oder Dienste enthält, auf die ich aus meinem Skript zugreifen will.
Danach definiere ich meinen Prozess(UseCase), der einen Anwendungfall repräsentiert. In meinem Bespiel kann jeder Prozess eine Liste(subProcesses) mit Teilprozessen(auch vom Typ IProcess) beinhalten.
Ein Teilprozess ist mein Skript(UserSpecificScript) was zusätzlich zu dem Code den ich für meinen Anwendungfall(UseCase) in der run Methode implementiere ausgeführt werden soll. Das Skript ist einfach gehalten und weist der Variable text, in dem Kontext, den Wert "Hello Bubu!" zu.
In dem trait (interface) IScript ist eine Variable scriptsDirectory definiert. Diese verwende ich um die Pfade zu den Skripten dynamisch zusammen zu bauen. Der Pfad setzt sich aus scriptsDirectory + ClassName + .scala zusammen, wobei der ClassName der Name der Klasse ohne dem Package ist.
Das Skript(UserSpecificScript.scala) selbst hab ich bei mir unter einem Pfad(/home/.../scripts abgelegt und einen Datei-Link(File>New>File>Advanced>Link to file in the file system) in Eclipse definiert. Somit kann das Skript aus Eclipse mit Syntax-Highlighter editieren werden.
Hier ist die Ausgabe nach dem ich das Skript, während die Anwendung schläft(Thread.sleep), bearbeitet habe.
Eine mögliche Verbesserung von dem Bespiel wäre: das Cachen der Classfiles, die der Scala-Compiler beim Interpretieren in einem temporären Verzeichnis(/tmp/scalaint*****/) ablegt. Damit man ein Skript NUR neu interpretiert, sobald es sich ändert und nicht ständig den Interpreter bei größeren Skripten quälen muss.
Skript:
Geändertes Skript:
Code:
Gruß Konstantin
hier ist ein kleines Beispiel, dass zeigen soll, wie einfach man in seine kompilierte Scala-Anwendung, den Scala-Interpreter einbauen kann, um bestimmte Bereiche der Anwendung flexibel zu halten. Die Flexibilität erfolgt über in Scala geschriebene Skripte, die zur Laufzeit dynamisch kompiliert (interpretiert) werden. In dem Beispiel wird auch gezeigt, wie man bestimmte Kontexte an ein Skript bzw. an den Interpreter bindet. Damit lassen sich die Kontexte durch Skripte abrufen und manipulieren.
Umgebung:
Eclipse 3.4
Scala Plugin Beta (nightly build)
Scala 2.7.1 Final (scala-library.jar, scala-compiler.jar)
Beispiel:
In dem Beispiel erstelle ich einen Kontext(UserContext), der bestimmte Informationen oder Dienste enthält, auf die ich aus meinem Skript zugreifen will.
Danach definiere ich meinen Prozess(UseCase), der einen Anwendungfall repräsentiert. In meinem Bespiel kann jeder Prozess eine Liste(subProcesses) mit Teilprozessen(auch vom Typ IProcess) beinhalten.
Ein Teilprozess ist mein Skript(UserSpecificScript) was zusätzlich zu dem Code den ich für meinen Anwendungfall(UseCase) in der run Methode implementiere ausgeführt werden soll. Das Skript ist einfach gehalten und weist der Variable text, in dem Kontext, den Wert "Hello Bubu!" zu.
In dem trait (interface) IScript ist eine Variable scriptsDirectory definiert. Diese verwende ich um die Pfade zu den Skripten dynamisch zusammen zu bauen. Der Pfad setzt sich aus scriptsDirectory + ClassName + .scala zusammen, wobei der ClassName der Name der Klasse ohne dem Package ist.
Das Skript(UserSpecificScript.scala) selbst hab ich bei mir unter einem Pfad(/home/.../scripts abgelegt und einen Datei-Link(File>New>File>Advanced>Link to file in the file system) in Eclipse definiert. Somit kann das Skript aus Eclipse mit Syntax-Highlighter editieren werden.
Hier ist die Ausgabe nach dem ich das Skript, während die Anwendung schläft(Thread.sleep), bearbeitet habe.
Code:
performs script: userSpecificScript
loaded script: context.text = "Hello Bubu!"
context: de.kde.tutorials.scalaInterpreter.UserContext = [UserContext text=]
[UserContext text=Hello Bubu!]
performs script: userSpecificScript
loaded script: context.text = "Hello Bambi!"
context: de.kde.tutorials.scalaInterpreter.UserContext = [UserContext text=Hello Bubu!]
[UserContext text=Hello Bambi!]
Eine mögliche Verbesserung von dem Bespiel wäre: das Cachen der Classfiles, die der Scala-Compiler beim Interpretieren in einem temporären Verzeichnis(/tmp/scalaint*****/) ablegt. Damit man ein Skript NUR neu interpretiert, sobald es sich ändert und nicht ständig den Interpreter bei größeren Skripten quälen muss.
Skript:
Java:
context.text = "Hello Bubu!"
Geändertes Skript:
Java:
context.text = "Hello Bambi!"
Code:
Java:
package de.kde.tutorials.scalaInterpreter
import scala.tools.nsc._
import java.io._
/*
* @author konstantin.denerz
*/
object InterpreterExample {
def main(args : Array[String]) : Unit = {
// define user context
var context = new UserContext
// define use case
var useCase = new UseCase
// run the process
useCase.run(context)
// show the changed context
println(context)
// sleep during script manipulation
Thread sleep 5000
useCase.run(context)
println(context)
}
}
/*
* the context used to run a process
*/
trait IContext {}
/*
* User context
*/
class UserContext extends IContext{
var _text = ""
// getter
def text = {_text}
// setter
def text_=(theText: String) {
_text = theText
}
override def toString() : String = { "[UserContext text=" + text + "]" }
}
trait IProcess {
var subProcesses = Map[String, IProcess]()
def run(context: IContext) {
// run all scripts for this use case
subProcesses.keys.foreach(key =>
{
println("performs script: " + key)
subProcesses(key).run(context)
}
)
}
}
/*
* Interface for all scripts
*/
trait IScript extends IProcess {
/*
* path to directory which contains the scala script files
*/
val scriptsDirectory = "/home/kde/private/projects/example/scripts"
// settings for the scala interpreter & compiler
val settings = new Settings(null)
var interpreter : Interpreter = _
/*
* create a new instance of scala interpreter
*/
def createInterpreter() {
settings.classpath.value = List(settings.classpath.value,"")
.mkString("",java.io.File.separator,"")
interpreter = new Interpreter(settings)
}
/*
* read the script from file
*/
def getScript() : String = {
var result = ""
// get the class name for instance : Bubu
var className = this.getClass.getSimpleName
// read scala file
var file = new File(List(scriptsDirectory, className)
.mkString("", "/", ".scala"))
var reader = new BufferedReader(new FileReader(file))
try{
var line = ""
do{
line = reader.readLine
if(null != line){
result+=line
}
}while(line != null)
}catch{
case e : Throwable => e.printStackTrace
case _=>
}
println("loaded script: " + result)
result
}
override def run(context: IContext) {
var script = getScript
// make the context reachable for the script
interpreter.bind("context", context.getClass.getName, context)
// interpret the script
interpreter.interpret(script)
super.run(context)
}
}
class UseCase extends IProcess {
subProcesses = Map("userSpecificScript" -> new UserSpecificScript)
override def run(context: IContext) {
// ...
super.run(context)
}
}
class UserSpecificScript extends IScript{
createInterpreter()
override def run(context: IContext) {
// ...
// run the script
super.run(context)
}
}
Gruß Konstantin
Zuletzt bearbeitet von einem Moderator: