ERLEDIGT
JA
ANTWORTEN
2
ZUGRIFFE
8799
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
  1. #1
    Konstantin Denerz Tutorials.de Gastzugang
    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.
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    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:

    Code java:
    1
    
    context.text = "Hello Bubu!"

    Geändertes Skript:

    Code java:
    1
    
    context.text = "Hello Bambi!"

    Code:

    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    
    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
    Geändert von Konstantin Denerz (08.08.08 um 09:45 Uhr) Grund: spelling mistakes ;-)
     

  2. #2
    Registriert seit
    Jun 2002
    Ort
    Saarbrücken (Saarland)
    Beiträge
    10.222
    Hallo,

    schickes Beispiel Im Prinzip ist hier der springende Punkt das man beliebige Objekte an den Scala Interpreter binden und somit eine "Kommunikation" zwischen Script Umgebung und "äußerer" Laufzeit möglich ist.

    Man siehe auch:
    http://www.jugsaar.de/?p=131

    Hier noch ein Beispiel wie man von Java aus ein Objekt an ein Scala Script übergeben kann:

    Hier unser scala Script in einem separeten Scala Projekt:
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    
    package de.tutorials.scala2
     
    object Test {
      def main(args:Array[String])={
        println(args.toList)
        args(0)="Scala"
      }
    }

    Hier rufen wir das Scala Script aus einer java Anwendung aus auf:
    (Hier rufe ich eine Methode einer schon kompilierten Scala Klasse auf. Die entsprechenden .class Files müssen natürlich auch im Classpath liegen, bei mir habe ich dazu den Class Folder (bin) des entsprechenden Scalaprojektes im Java Buildpath des Javaprojektes eingebunden)
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    /**
     * 
     */
    package de.tutorials;
     
    import scala.tools.nsc.Interpreter;
    import scala.tools.nsc.Settings;
     
    /**
     * @author Thomas.Darimont
     * 
     */
    public class ScalaInterpreterExample {
     
        /**
         * @param args
         */
        public static void main(String[] args) throws Exception {
            Settings settings = new Settings();
            System.out.println(settings.classpath());
     
     
            Interpreter interpreter = new Interpreter(settings);
     
            String[] context = { "FOO" };
            interpreter.bind("context", "Array[String]", context);
            interpreter
                    .interpret("de.tutorials.scala2.Test.main(context)");
            
            context[0] = "BAR";
            
            interpreter
                    .interpret("de.tutorials.scala2.Test.main(context)");
            interpreter
                    .interpret("binder0.getClass.getProtectionDomain.getCodeSource.getLocation");
            
            System.out.println(context[0]);
        }
    }

    Ausgabe:
    Code :
    1
    2
    3
    4
    5
    6
    
    StringSetting(-classpath,path,Specify where to find user class files,D:\eclipse\workspaces\workspace-3.4\de.tutorials.training\bin;D:\stuff\jtds\jtds-1.2.2.jar;D:\stuff\neo4j\neo-1.0-b6\neo-1.0-b6.jar;D:\stuff\neo4j\neo-1.0-b6\jta-spec1_0_1.jar;D:\eclipse\3.4\eclipse\plugins\org.eclipse.team.svn_0.7.1.I20080612-1500.jar;D:\eclipse\3.4\eclipse\plugins\org.eclipse.team.svn.core_0.7.1.I20080612-1500.jar;D:\eclipse\3.3M7\eclipse\plugins\org.polarion.team.client.javasvn.standard.core_1.1.2\lib\svnkit.jar;D:\eclipse\3.3M7\eclipse\plugins\org.polarion.team.client.javasvn.standard.core_1.1.2\lib\svnkit-javahl.jar;D:\stuff\log4j\1.2\apache-log4j-1.2.15\log4j-1.2.15.jar;D:\stuff\scala\2.7.1\scala-2.7.1.final\lib\scala-library.jar;D:\stuff\scala\2.7.1\scala-2.7.1.final\lib\sbaz.jar;D:\stuff\scala\2.7.1\scala-2.7.1.final\lib\sbaz-tests.jar;D:\stuff\scala\2.7.1\scala-2.7.1.final\lib\scala-compiler.jar;D:\stuff\scala\2.7.1\scala-2.7.1.final\lib\scala-dbc.jar;D:\stuff\scala\2.7.1\scala-2.7.1.final\lib\scala-decoder.jar;D:\eclipse\workspaces\workspace-3.4\de.tutorials.scala2\bin)
    context: Array[String] = Array(FOO)
    List(FOO)
    List(BAR)
    res2: java.net.URL = file:/C:/DOKUME~1/THOMAS~1.DAR/LOKALE~1/Temp/scalaint47770/
    Scala

    Gruß Tom
     
    Du kommst aus dem Saarland oder Umgebung? Du hast Interesse an Java-Technologie Themen? Then let's Meetup!

    Java rocks! http://www.jugsaar.de

    Does IT in Java and .Net
    Xing
    Twitter

  3. #3
    Konstantin Denerz Tutorials.de Gastzugang
    Hallo,

    hier eine kleine Erweiterung des Beispiels. Dabei werden zwar nicht die Classfiles gecachet, jedoch wird verhindert, dass ein Skript jedes mal neu interpretiert/kompiliert wird, bis man es wünscht.

    hier das geänderte Trait:
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    
    /*
     * Interface for all scripts
     */
    trait IScript extends IProcess {
      
      // ....
      
      /*
       * read the script
       */
      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"))
        
        if(isDirty){
          result = getScriptRunner + System.getProperty("line.separator") + getScriptFrom(file)
        }else{
        // run the runner only
          result = getScriptRunner()
        }
        
        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)
        
        isDirty = false
        
      }
      
      var isDirty = true
      
      def getScriptRunner() = {
        "Runner.run"
      }
      
      def getScriptFrom(file: File) = {
        var result = ""
        var reader = new BufferedReader(new FileReader(file))
        try{
          
          var line = ""
          do{
            line = reader.readLine
            if(null != line){
              result += line + System.getProperty("line.separator")
            }
          }while(line != null)
            
        }catch{
          case e : Throwable => e.printStackTrace
          case _=> 
        }
        result
      }
      
    }


    Und hier das neue Skript. Dabei ist eine Klasse(MyScript) zu sehen, die eine Methode zum Manipulieren des Kontexts hat. Wenn der Interpreter dieses Skript einmal interpretiert (kompiliert), dann werden die Classfiles dafür generiert. Solange das Skript sich nicht ändert, kann man den Runner zum Ausführen des Skript verwenden. In run Methode des IScript traits wird dann statt dem ganzen Skript, einfach nur der Aufruf Runner.run interpretiert. Somit ruft immer das gleiche Skript, ohne es neu zu kompilieren, auf.

    UserSpecificScript.scala:
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    object Runner{
      def run = {
        var script = new MyScript
        script.changeContext(context)
      }
    }
     
    import de.kde.tutorials.scalaInterpreter._
     
    class MyScript{
     
      def changeContext(theContext : IContext) = {
         theContext.asInstanceOf[UserContext].text = "Hello Bambi!"
      }
      
    }

    Hier die Ausgabe von dem Ganzen, woran man erkennt, dass beim zweiten Aufruf nur der Context nochmal an den Interpreter gebunden und der Runner ausgeführt wird, ohne das module Runner und MySkript neu definiert (kompiliert) werden.

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
    performs script: userSpecificScript
    loaded script: Runner.run
    object Runner{
      def run = {
        var script = new MyScript
        script.changeContext(context)
      }
    }
     
    import de.kde.tutorials.scalaInterpreter._
     
    class MyScript{
     
      def changeContext(theContext : IContext) = {
         theContext.asInstanceOf[UserContext].text = "Hello Bambi!"
      }
      
    }
     
    context: de.kde.tutorials.scalaInterpreter.UserContext = [UserContext text=]
    defined module Runner
    import de.kde.tutorials.scalaInterpreter._
    defined class MyScript
    [UserContext text=Hello Bambi!]
     
     
     
     
     
    performs script: userSpecificScript
    loaded script: Runner.run
    context: de.kde.tutorials.scalaInterpreter.UserContext = [UserContext text=Hello Bambi!]
    [UserContext text=Hello Bambi!]


    Gruß Konstantin
     

Ähnliche Themen

  1. Scala Tutorial
    Von vogella im Forum Java Technology News
    Antworten: 0
    Letzter Beitrag: 11.10.09, 08:14
  2. Eclipse Plugins mit Scala
    Von Thomas Darimont im Forum Java
    Antworten: 0
    Letzter Beitrag: 24.06.08, 10:04
  3. Interessanter IBM Artikel zu Scala und XML
    Von Thomas Darimont im Forum Java Technology News
    Antworten: 0
    Letzter Beitrag: 23.04.08, 10:37
  4. Interessanter IBM Artikel zu Scala
    Von Thomas Darimont im Forum Java Technology News
    Antworten: 0
    Letzter Beitrag: 23.01.08, 16:10