You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2010/02/01 20:37:26 UTC

svn commit: r905388 [3/3] - in /sling/trunk/contrib/scripting/scala: config/ config/src/ config/src/main/ config/src/main/java/ config/src/main/java/org/ config/src/main/java/org/apache/ config/src/main/java/org/apache/sling/ config/src/main/java/org/a...

Added: sling/trunk/contrib/scripting/scala/script/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaInterpreter.scala
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/script/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaInterpreter.scala?rev=905388&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/scala/script/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaInterpreter.scala (added)
+++ sling/trunk/contrib/scripting/scala/script/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaInterpreter.scala Mon Feb  1 19:37:24 2010
@@ -0,0 +1,374 @@
+/*
+ * 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.
+ */
+import scala.tools.nsc.{Settings, Global}
+import scala.tools.nsc.interpreter.AbstractFileClassLoader
+import scala.tools.nsc.io.AbstractFile
+import scala.tools.nsc.reporters.Reporter
+import scala.tools.nsc.util.{SourceFile, BatchSourceFile}
+import java.net.URLClassLoader
+import java.io.{File, InputStream, OutputStream}
+import org.apache.sling.scripting.scala.Utils.{option}
+
+package org.apache.sling.scripting.scala.interpreter {
+
+/**
+ * An interpreter for Scala scripts. Interpretation of scripts proceeds in the following steps:
+ * <ol>
+ * <li>Pre-compilation: The source script is {@link #preProcess} wrapped into a wrapper which
+ *   contains variable definitions of the approproate types for the passed {@link Bindings bindings}.</li>
+ * <li>Compilation: The resulting source code is {@link #compile compiled} by the Scala compiler. </li>
+ * <li>Execution: The class file is {@link #execute loaded} and its main method called.</li>
+ * </ol>
+ * @param settings  compiler settings
+ * @param reporter  reporter for compilation
+ * @param classes  additional classes for the classpath
+ * @param outDir  ourput directory for the compiler
+ */
+class ScalaInterpreter(settings: Settings, reporter: Reporter, classes: Array[AbstractFile],
+                       outDir: AbstractFile) {
+
+  /**
+   * Same as <code>ScalaInterpreter(settings, reporter, classes, null)</code>.
+   * @param settings
+   * @param reporter
+   * @param classes
+   * @return
+   */
+  def this(settings: Settings, reporter: Reporter, classes: Array[AbstractFile]) =
+    this(settings, reporter, classes, null)
+
+  /**
+   * Same as <code>ScalaInterpreter(settings, reporter, null, outDir)</code>.
+   * @param settings
+   * @param reporter
+   * @param outDir
+   * @return
+   */
+  def this(settings: Settings, reporter: Reporter, outDir: AbstractFile) =
+    this(settings, reporter, null, outDir)
+
+  /**
+   * Same as <code>ScalaInterpreter(settings, reporter, null, null)</code>.
+   * @param settings
+   * @param reporter
+   * @return
+   */
+  def this(settings: Settings, reporter: Reporter) =
+    this(settings, reporter, null, null)
+
+  /**
+   * The parent class loader used for execution
+   */
+  protected val parentClassLoader: ClassLoader = getClass.getClassLoader
+
+  /**
+   * The Scala compiler used for compilation
+   */
+  protected val compiler: Global = {
+    val c = new ScalaCompiler(settings, reporter, classes)
+    if (outDir != null) c.genJVM.outputDir = outDir
+    c
+  }
+
+  /**
+   * Generates a wrapper which contains variables declarations and implicit conversions
+   * to make the bindings visible on all accessible types. 
+   * @param name  name of the script. Used for generating the class name of the wrapper.
+   * @param code  source code of the script
+   * @param bindings  bindings to be passed to the script
+   * @return  a valid Scala source
+   * @throws InterpreterException
+   */
+  @throws(classOf[InterpreterException])
+  protected def preProcess(name: String, code: String, bindings: Bindings): String = {
+    val NL: String = System.getProperty("line.separator")
+
+    def packetize(name: String): List[String] = name.split('.').toList
+    def mangle(name: String) = packetize(name).mkString("_")
+
+    def bind(arg: (String, AnyRef)) = {
+      val views = bindings.getViews(arg._2.getClass)
+      val className = views.head.getName
+      val implicits = 
+        for {
+          view <- views.tail
+          intfName = view.getName
+          methName = mangle(className) + "2" + mangle(intfName)  
+        }
+        yield 
+          "    implicit def " + methName + "(x: " + className + "): " + intfName + " = x.asInstanceOf[" +  intfName + "]"
+      
+      "    lazy val " + arg._1 + " = bindings.getValue(\"" + arg._1 + "\").asInstanceOf[" + className + "]" + NL +
+      implicits.mkString(NL)
+    }
+
+    val compounds = packetize(name)
+    val className = compounds.last
+
+    def packageDeclaration =
+      if (compounds.size > 1) compounds.init.mkString("package ", ".", "") + NL
+      else throw new InterpreterException("Default package not allowed: " + name)
+
+    code + NL + 
+    packageDeclaration + " {" + NL + 
+    "  class " + className + "Vars(bindings: org.apache.sling.scripting.scala.interpreter.Bindings) { " + NL +
+         bindings.map(bind).mkString(NL) + NL + 
+    "  } " + NL + 
+    "  object " + className + "Runner {" + NL +
+    "    def main(bindings: org.apache.sling.scripting.scala.interpreter.Bindings," + NL +
+    "             stdIn: java.io.InputStream," + NL +
+    "             stdOut: java.io.OutputStream) {" + NL +
+    "      Console.withIn(stdIn) {" + NL +
+    "        Console.withOut(stdOut) {" + NL +
+    "          new " + className + "(new " + className + "Vars(bindings))" + NL +
+    "          stdOut.flush" + NL +
+    "        }" + NL +
+    "      }" + NL +
+    "    }" + NL +
+    "  }" + NL + 
+    "}" + NL
+  }
+
+  /**
+   * Compiles a list of source files. No pre-processing takes place.
+   * @param sources  source files
+   * @return  result of compilation
+   */
+  protected def compile(sources: List[SourceFile]): Reporter = {
+    reporter.reset
+    val run = new compiler.Run
+    if (reporter.hasErrors)
+      reporter
+    else {
+      run.compileSources(sources)
+      reporter
+    }
+  }
+
+  /**
+   * Compiles a single source file. No pre-processing takes place.
+   * @param name  name of the script
+   * @param code  source code
+   * @return  result of compilation
+   */
+  def compile(name: String, code: String): Reporter =
+    compile(List(new BatchSourceFile(name, code.toCharArray)))
+
+  /**
+   * Pre-processes and compiles a single source file.
+   * @param name  name of the script
+   * @param code  source code
+   * @param bindings  variable bindings to pass to the script
+   * @return  result of compilation
+   */
+  def compile(name: String, code: String, bindings: Bindings): Reporter =
+    compile(name, preProcess(name, code, bindings))
+
+  /**
+   * Compiles a single source file. No pre-processing takes place.
+   * @param sources  source file
+   * @return  result of compilation
+   */
+  def compile(source: AbstractFile): Reporter = {
+    compile(List(new BatchSourceFile(source)))
+  }
+
+  /**
+   * Pre-processes and compiles a single source file.
+   * @param name  name of the script
+   * @param source  source file
+   * @param bindings  variable bindings to pass to the script
+   * @return  result of compilation
+   */
+  def compile(name: String, source: AbstractFile, bindings: Bindings): Reporter = {
+    val code = new String(source.toByteArray)
+    compile(name, preProcess(name, code, bindings))
+  }
+
+  /**
+   * Interprete a script
+   * @param name  name of the script
+   * @param code  source code
+   * @param bindings  variable bindings to pass to the script
+   * @param in  stdIn for the script execution
+   * @param out  stdOut for the script execution
+   * @return  result of execution
+   * @throws InterpreterException
+   */
+  @throws(classOf[InterpreterException])
+  def interprete(name: String, code: String, bindings: Bindings, in: Option[InputStream],
+                 out: Option[OutputStream]): Reporter = {
+    compile(name, code, bindings)
+    if (reporter.hasErrors)
+      reporter
+    else {
+      execute(name, bindings, in, out)
+    }
+  }
+
+  /**
+   * Same as <code>interprete(name, code, bindings, None, None)</code>.
+   * @param name  name of the script
+   * @param code  source code
+   * @param bindings  variable bindings to pass to the script
+   * @return  result of execution
+   * @throws InterpreterException
+   */
+  @throws(classOf[InterpreterException])
+  def interprete(name: String, code: String, bindings: Bindings): Reporter =
+    interprete(name, code, bindings, None, None)
+
+  /**
+   * Same as <code>interprete(name, code, bindings, Some(in), Some(out))</code>.
+   * @param name  name of the script
+   * @param code  source code
+   * @param bindings  variable bindings to pass to the script
+   * @param in  stdIn for the script execution
+   * @param out  stdOut for the script execution
+   * @return  result of execution
+   * @throws InterpreterException
+   */
+  @throws(classOf[InterpreterException])
+  def interprete(name: String, code: String, bindings: Bindings, in: InputStream,
+                 out: OutputStream): Reporter =
+    interprete(name, code, bindings, option(in), option(out))
+
+  /**
+   * Interprete a script
+   * @param name  name of the script
+   * @param source source file
+   * @param bindings  variable bindings to pass to the script
+   * @param in  stdIn for the script execution
+   * @param out  stdOut for the script execution
+   * @return  result of execution
+   * @throws InterpreterException
+   */
+  @throws(classOf[InterpreterException])
+  def interprete(name: String, source: AbstractFile, bindings: Bindings, in: Option[InputStream],
+                 out: Option[OutputStream]): Reporter = {
+    compile(name, source, bindings)
+    if (reporter.hasErrors)
+      reporter
+    else {
+      execute(name, bindings, in, out)
+    }
+  }
+
+  /**
+   * Same as <code>interprete(name, code, bindings, None, None)</code>.
+   * @param name  name of the script
+   * @param source source file
+   * @param bindings  variable bindings to pass to the script
+   * @return  result of execution
+   * @throws InterpreterException
+   */
+  @throws(classOf[InterpreterException])
+  def interprete(name: String, source: AbstractFile, bindings: Bindings): Reporter =
+    interprete(name, source, bindings, None, None)
+
+  /**
+   * Same as <code>interprete(name, code, bindings, Some(in), Some(out))</code>.
+   * @param name  name of the script
+   * @param source source file
+   * @param bindings  variable bindings to pass to the script
+   * @param in  stdIn for the script execution
+   * @param out  stdOut for the script execution
+   * @return  result of execution
+   * @throws InterpreterException
+   */
+  @throws(classOf[InterpreterException])
+  def interprete(name: String, source: AbstractFile, bindings: Bindings, in: InputStream,
+                 out: OutputStream): Reporter =
+    interprete(name, source, bindings, option(in), option(out))
+
+  /**
+   * Looks up the class file for a compiled script
+   * @param  name  script name
+   * @return  the class file or null if not found
+   */
+  def getClassFile(name: String): AbstractFile = {
+    var file: AbstractFile = compiler.genJVM.outputDir
+    val pathParts = name.split("[./]").toList
+    for (dirPart <- pathParts.init) {
+      file = file.lookupName(dirPart, true)
+      if (file == null) {
+        return null
+      }
+    }
+    file.lookupName(pathParts.last + ".class", false)
+  }
+
+  /**
+   * Executes a compiled script
+   * @param name  name of the script
+   * @param bindings  variable bindings to pass to the script
+   * @param in  stdIn for the script execution
+   * @param out  stdOut for the script execution
+   * @return  result of execution
+   * @throws  InterpreterException when class files cannot be accessed or nor entry point is found
+   */
+  @throws(classOf[InterpreterException])
+  def execute(name: String, bindings: Bindings, in: Option[InputStream], out: Option[OutputStream]): Reporter = {
+    try {
+      val classLoader = new AbstractFileClassLoader(compiler.genJVM.outputDir, parentClassLoader)
+      val script = Class.forName(name + "Runner", true, classLoader)
+      val initMethod = (script
+        .getDeclaredMethods
+        .toList
+        .find(method => method.getName == "main")
+        .get)
+
+      initMethod.invoke(null, Array(bindings, in.getOrElse(java.lang.System.in),
+                                              out.getOrElse(java.lang.System.out)): _*)
+      reporter
+    }
+    catch {
+      case e: java.lang.reflect.InvocationTargetException =>
+        throw new InterpreterException("Error executing " + name, e.getTargetException)
+      case e: Exception =>
+        throw new InterpreterException("Error executing " + name, e)
+    }
+  }
+
+  /**
+   * Same as <code>execute(name, bindings, None, None)</code>.
+   * @param name  name of the script
+   * @param bindings  variable bindings to pass to the script
+   * @return  result of execution
+   * @throws  InterpreterException when class files cannot be accessed or nor entry point is found
+   */
+  @throws(classOf[InterpreterException])
+  def execute(name: String, bindings: Bindings): Reporter =
+    execute(name, bindings, None, None)
+
+  /**
+   * Same as <code>execute(name, bindings, Some(in), Some(out))</code>.
+   * @param name  name of the script
+   * @param bindings  variable bindings to pass to the script
+   * @param in  stdIn for the script execution
+   * @param out  stdOut for the script execution
+   * @return  result of execution
+   * @throws  InterpreterException when class files cannot be accessed or nor entry point is found
+   */
+  @throws(classOf[InterpreterException])
+  def execute(name: String, bindings: Bindings, in: InputStream, out: OutputStream): Reporter =
+    execute(name, bindings, option(in), option(out))
+
+}
+
+
+}
\ No newline at end of file

Added: sling/trunk/contrib/scripting/scala/script/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaSettings.scala
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/script/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaSettings.scala?rev=905388&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/scala/script/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaSettings.scala (added)
+++ sling/trunk/contrib/scripting/scala/script/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaSettings.scala Mon Feb  1 19:37:24 2010
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+import scala.tools.nsc.Settings
+
+package org.apache.sling.scripting.scala.interpreter {
+
+/**
+ * Utility to parse Scala compiler settings from a string. This class
+ * can be used from Java instead of Settings. Settings is not accessible
+ * from Java since scala.Nothing is not visible exposed. 
+ * See https://lampsvn.epfl.ch/trac/scala/ticket/1254
+ */  
+class ScalaSettings(error: String => Unit) extends Settings(error) {
+  def this() = this(Console.println)
+  
+  def parse(line: String) = {
+    def error(s: String): Nothing = throw new InterpreterException(s)
+    parseParams(line, error)
+  }
+  
+}
+
+}

Added: sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/JcrFSTest.scala
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/JcrFSTest.scala?rev=905388&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/JcrFSTest.scala (added)
+++ sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/JcrFSTest.scala Mon Feb  1 19:37:24 2010
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+import java.io.{PrintWriter, InputStreamReader}
+import javax.jcr.{Session, Repository, Node, SimpleCredentials}
+import junit.framework.TestCase
+import junit.framework.Assert.{assertEquals, assertTrue, assertFalse}
+import org.apache.sling.scripting.scala.JcrFS.{JcrNode, JcrFile, JcrFolder}
+import org.apache.jackrabbit.core.{TransientRepository}
+
+package org.apache.sling.scripting.scala {
+
+class JcrFSTest extends TestCase {
+  var session: Session = null
+  var repository: Repository = null
+  var testRoot: Node = null
+
+  override def setUp() {
+    super.setUp()
+    repository = new TransientRepository
+    session = repository.login(new SimpleCredentials("admin", "admin".toCharArray))
+    testRoot = session.getRootNode.addNode("testRoot", "nt:folder")
+    session.save()
+  }
+
+  override def tearDown() {
+    testRoot.remove()
+    testRoot = null
+    session.save()
+    session.logout()
+    session = null
+    repository = null
+    super.tearDown()
+  }
+
+  def testTraverse: Unit = {
+    def traverse(entry: JcrNode): String = {
+      (for (entry <- entry.elements) yield {entry match {
+        case file: JcrFile => file.path
+        case folder: JcrFolder => folder.path + traverse(folder)
+      }})
+      .mkString("(", ",", ")")
+    }
+
+    var _id = 0
+    def id() = {
+      _id += 1
+      _id
+    }
+
+    def addChildren(folder: Node) = {
+      folder.addNode("file" + id(), "nt:file")
+      folder.addNode("file" + id(), "nt:file")
+      (folder.addNode("folder" + id(), "nt:folder"), folder.addNode("folder" + id(), "nt:folder"))
+    }
+
+    val (f1, f2) = addChildren(testRoot)
+    val (f3, f4) = addChildren(f1)
+    addChildren(f2)
+    addChildren(f3)
+    addChildren(f4)
+    val actual = traverse(JcrFS.create(testRoot))
+    val expected =
+      "(/testRoot/file1,/testRoot/file2,/testRoot/folder3(/testRoot/folder3/file5,/testRoot/folder3/file6," +
+      "/testRoot/folder3/folder7(/testRoot/folder3/folder7/file13,/testRoot/folder3/folder7/file14," +
+      "/testRoot/folder3/folder7/folder15(),/testRoot/folder3/folder7/folder16())," +
+      "/testRoot/folder3/folder8(/testRoot/folder3/folder8/file17,/testRoot/folder3/folder8/file18," +
+      "/testRoot/folder3/folder8/folder19(),/testRoot/folder3/folder8/folder20()))," +
+      "/testRoot/folder4(/testRoot/folder4/file9,/testRoot/folder4/file10,/testRoot/folder4/folder11()," +
+      "/testRoot/folder4/folder12()))"
+    assertEquals(expected, actual)
+  }
+
+  def testCreateFile {
+    val root = JcrFS.create(testRoot)
+    val file = root.fileNamed("file")
+    val fileNode = testRoot.getNode("file")
+    assertFalse(file.isDirectory)
+    assertEquals("nt:file", fileNode.getPrimaryNodeType.getName)
+    assertEquals("file", file.name)
+    assertEquals("/testRoot/file", file.path)
+    assertEquals(fileNode.getProperty("jcr:content/jcr:lastModified").getLong, file.lastModified)
+    assertEquals(fileNode.getProperty("jcr:content/jcr:data").getLength, file.sizeOption.get.toLong)
+
+    val contentNode = fileNode.getNode("jcr:content")
+    assertEquals("nt:resource", contentNode.getPrimaryNodeType.getName)
+
+    val input = file.input
+    assertEquals(0, input.available)
+    assertEquals(-1, input.read)
+  }
+
+  def testCreateFolder {
+    val root = JcrFS.create(testRoot)
+    val folder = root.subdirectoryNamed("folder")
+    val folderNode = testRoot.getNode("folder")
+    assertTrue(folder.isDirectory)
+    assertEquals(0L, folder.lastModified)
+    assertEquals("nt:folder", folderNode.getPrimaryNodeType.getName)
+    assertEquals("folder", folder.name)
+    assertEquals("/testRoot/folder", folder.path)
+  }
+
+  def testParent {
+    val root = JcrFS.create(testRoot)
+    val folder = root.subdirectoryNamed("folder")
+    val file = folder.fileNamed("file")
+    assertEquals(folder, file.container)
+  }
+
+  def testReadWriteContent {
+    val root = JcrFS.create(testRoot)
+    val file = root.fileNamed("file")
+    val contentNode = testRoot.getNode("file/jcr:content")
+
+    val writer = new PrintWriter(file.output)
+    writer.print("Hello world")
+    writer.close
+    assertEquals("Hello world", contentNode.getProperty("jcr:data").getString)
+    assertEquals(11, file.sizeOption.get)
+
+    val reader = new InputStreamReader(file.input)
+    val c = new Array[char](32)
+    reader.read(c)
+    assertEquals("Hello world", new String(c, 0, 11))
+  }
+
+}
+
+}

Added: sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/ScalaScriptEngineFactoryTest.scala
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/ScalaScriptEngineFactoryTest.scala?rev=905388&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/ScalaScriptEngineFactoryTest.scala (added)
+++ sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/ScalaScriptEngineFactoryTest.scala Mon Feb  1 19:37:24 2010
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+import javax.script.{
+  ScriptEngine,
+  ScriptEngineFactory}
+
+import junit.framework.TestCase
+import junit.framework.Assert._
+
+package org.apache.sling.scripting.scala {
+
+class ScalaScriptEngineFactoryTest extends TestCase {
+
+  def testScriptEngineFactoryInit() {
+    val scalaEngineFactory = new ScalaScriptEngineFactory
+    assertNotNull(scalaEngineFactory)
+  }
+  
+  def testScriptEngineFactoryEngine() {
+    try {
+      val scriptEngine = (new ScalaScriptEngineFactory).getScriptEngine
+      assertNotNull(scriptEngine)
+    }
+    catch {
+      case e: IllegalStateException => // expected
+    }
+  }
+
+  def testScriptEngineFactoryLanguage() {
+    val language = (new ScalaScriptEngineFactory).getLanguageName
+      assertEquals("Scala", language)
+  }
+
+  def testScriptEngineFactoryLanguageVersion() {
+    val version = (new ScalaScriptEngineFactory).getLanguageVersion()
+    assertEquals("2.7.7", version)
+  }
+  
+}
+
+}
\ No newline at end of file

Added: sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterHelper.scala
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterHelper.scala?rev=905388&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterHelper.scala (added)
+++ sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterHelper.scala Mon Feb  1 19:37:24 2010
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+import java.io.{ByteArrayOutputStream, IOException, BufferedReader, FileReader, File}
+import java.net.URISyntaxException
+
+import javax.script.ScriptException
+
+import org.apache.sling.scripting.scala.interpreter.{ScalaInterpreter, Bindings}
+
+import scala.tools.nsc.Settings
+import scala.tools.nsc.io.AbstractFile
+import scala.tools.nsc.reporters.Reporter
+
+package org.apache.sling.scripting.scala.interpreter {
+
+/**
+ * Helper class for evaluating Scala scripts. 
+ */
+class InterpreterHelper(val srcDir: AbstractFile, val outDir: AbstractFile) {
+  require(srcDir != null)
+  require(outDir != null)
+  
+  val interpreter: ScalaInterpreter = createInterpreter
+  
+  private val interpreterOut = new ByteArrayOutputStream
+
+  @throws(classOf[ScriptException]) 
+  def eval(name: String, code: String, bindings: Bindings): String = {
+    try {
+      interpreterOut.reset
+      val result = interpreter.interprete(name, code, bindings, null, interpreterOut)
+      if (result.hasErrors) throw new ScriptException(result.toString())
+      interpreterOut.toString
+    }
+    catch {
+      case e: Exception => throw new ScriptException(e)
+    }
+  }
+
+  @throws(classOf[ScriptException]) 
+  def eval(name: String, src: AbstractFile, bindings: Bindings) = {
+    try {
+      interpreterOut.reset
+      val result = interpreter.interprete(name, src, bindings, null, interpreterOut)
+      if (result.hasErrors) throw new ScriptException(result.toString())
+      interpreterOut.toString
+    }
+    catch {
+      case e: Exception => throw new ScriptException(e)
+    }
+  }
+
+  // -----------------------------------------------------< protected >---
+  
+  protected def getSettings: Settings = new Settings();
+  protected def getClasspath: String = System.getProperty("java.class.path")
+  protected def getReporter(settings: Settings): Reporter = new BacklogReporter(settings); 
+
+  protected def createInterpreter: ScalaInterpreter = {
+    val settings = getSettings
+    settings.classpath.value = getClasspath
+    new ScalaInterpreter(settings, getReporter(settings), outDir)
+  }
+
+}
+
+}
\ No newline at end of file

Added: sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterTest.scala
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterTest.scala?rev=905388&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterTest.scala (added)
+++ sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterTest.scala Mon Feb  1 19:37:24 2010
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+import java.io.File
+
+import junit.framework.TestCase
+
+import org.apache.sling.scripting.scala.Utils.valueOrElse
+
+import scala.tools.nsc.io.PlainFile
+
+package org.apache.sling.scripting.scala.interpreter {
+
+/**
+ * Standard test cases where files are read/written to/from the file system.
+ */  
+class InterpreterTest extends TestCase with Tests {
+  
+  var interpreterHelper: InterpreterHelper = null;
+  
+  override def setUp() {
+    super.setUp();
+    
+    val workDir = new PlainFile(new File("target")).subdirectoryNamed("tmp")
+    val srcDir = workDir.subdirectoryNamed("src")
+    val outDir = workDir.subdirectoryNamed("classes")
+    
+    interpreterHelper = new InterpreterHelper(srcDir, outDir) {
+      override def getClasspath = valueOrElse(System.getProperty("surefire.test.class.path")) {
+        super.getClasspath
+      }
+    }
+  }
+  
+  override def tearDown() {
+    super.tearDown()
+    interpreterHelper = null
+  }
+
+}
+
+}
\ No newline at end of file

Added: sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterWithJcrTest.scala
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterWithJcrTest.scala?rev=905388&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterWithJcrTest.scala (added)
+++ sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterWithJcrTest.scala Mon Feb  1 19:37:24 2010
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+import javax.jcr.Node
+import javax.jcr.SimpleCredentials
+import javax.jcr.Session
+
+import junit.framework.TestCase
+import junit.framework.Assert.assertEquals
+
+import org.apache.jackrabbit.core.TransientRepository
+
+import org.apache.sling.scripting.scala.Utils.valueOrElse
+import org.apache.sling.scripting.scala.JcrFS
+
+package org.apache.sling.scripting.scala.interpreter {
+
+/** 
+ * JCR based test cases where files are read/written to/from a JCR repository 
+ * (Apache Jackrabbit).
+ */  
+class InterpreterWithJcrTest extends TestCase with Tests {
+  var interpreterHelper: InterpreterHelper = null
+  var repository: TransientRepository = null
+  var session: Session = null
+  var workNode: Node = null
+  
+  override def setUp() {
+    super.setUp();
+    
+    repository = new TransientRepository
+    session = repository.login(new SimpleCredentials("admin", "admin".toCharArray))
+    val rootNode = session.getRootNode
+    workNode = rootNode.addNode("scala_tests", "nt:folder");
+    workNode.getSession().save();
+    val workDir = JcrFS.create(workNode);
+    val srcDir = workDir.subdirectoryNamed("src")
+    val outDir = workDir.subdirectoryNamed("classes")
+    
+    interpreterHelper = new InterpreterHelper(srcDir, outDir) {
+      override def getClasspath = valueOrElse(System.getProperty("surefire.test.class.path")) {
+        super.getClasspath
+      }
+    }
+  }
+  
+  override def tearDown() {
+    interpreterHelper = null
+    session.logout()
+    repository.shutdown
+    repository = null
+    super.tearDown()
+  }
+
+  def testNodeAccess() {
+    val code = "package a { class Testi(vars: TestiVars) { import vars._; print(n.getPath)}}"
+    val bindings = Bindings()
+    bindings.putValue("n", workNode)
+    assertEquals(workNode.getPath, interpreterHelper.eval("a.Testi", code, bindings))
+  }
+  
+}
+
+}
\ No newline at end of file

Added: sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/Tests.scala
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/Tests.scala?rev=905388&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/Tests.scala (added)
+++ sling/trunk/contrib/scripting/scala/script/src/test/scala/org/apache/sling/scripting/scala/interpreter/Tests.scala Mon Feb  1 19:37:24 2010
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+import junit.framework.Assert.{assertEquals, assertFalse, fail}
+
+import java.io.PrintWriter
+
+import javax.script.ScriptException
+
+package org.apache.sling.scripting.scala.interpreter {
+
+/**
+ * Generic test cases. Implementors inject an InterpreterHelper instance.  
+ */  
+trait Tests {
+  var interpreterHelper: InterpreterHelper
+  
+  def testEvalString {
+    val code = "package a { class Testi(vars: TestiVars) { print(1 + 2) }}"
+    assertEquals("3", interpreterHelper.eval("a.Testi", code, Bindings()))
+  }
+
+  def testEvalError {
+    val code = "syntax error"
+    try {
+      interpreterHelper.eval("a.Testi", code, Bindings())
+      fail("Expecting ScriptException")
+    }
+    catch {
+      case _: ScriptException =>  // expected
+    }
+  }
+  
+  def testError {
+    val err = "Some error here";
+    val code = "package a { class Testi(vars: TestiVars) { throw new Error(\"" + err + "\") }}"
+    try {
+      interpreterHelper.eval("a.Testi", code, Bindings())
+      fail("Expecting Exception")
+    }
+    catch {
+      case e: ScriptException if err == e.getCause.getCause.getMessage => // expected
+    }
+  }
+
+  def testScalaInterpreter {
+    val bindings = Bindings()
+    val time = java.util.Calendar.getInstance.getTime
+    bindings.putValue("msg", "Hello world")
+    bindings.putValue("time", time)
+    val code = "package a { class Testi(vars: TestiVars) {import vars._; print(msg + \": \" + time)}}"
+    val result = interpreterHelper.eval("a.Testi", code, bindings)
+    assertEquals("Hello world: " + time, result)
+  }
+  
+  def testCompileExecute {
+    val srcDir = interpreterHelper.srcDir
+    val interpreter = interpreterHelper.interpreter
+
+    val bindings = Bindings()
+    val time = java.util.Calendar.getInstance.getTime
+    bindings.putValue("msg", "Hello world")
+    bindings.putValue("time", time)
+
+    val code = "package a { class Testi(vars: TestiVars) {import vars._; print(msg + \": \" + time)}}"
+    val src = srcDir.fileNamed("Testi.scala")
+    val writer = new PrintWriter(src.output)
+    writer.print(code)
+    writer.close
+
+    val out = new java.io.ByteArrayOutputStream
+    var result = interpreter.compile("a.Testi", src, bindings)
+    assertFalse(result.hasErrors)
+
+    result = interpreter.execute("a.Testi", bindings, None, Some(out))
+    assertFalse(result.hasErrors)
+
+    assertEquals("Hello world: " + time, out.toString)
+  }
+
+}
+
+}
\ No newline at end of file