1

I am writing a Scala application (that is supposed to run on Hadoop using Spark) and my users are to execute JavaScript snippets that they upload and I want to provide access to certain helper functions written in Scala (like "make an HTTP call" etc.) to these JavaScript users. So what I do is writing a big JavaScriptHelpers object and then give access to that object using

engine = scriptEngineManager.getEngineByName("JavaScript")
engine.put("jql", JavaScriptHelpers)

so users can say jql.httpPost(...) from within JavaScript. The Scala code that makes this possible looks as follows:

def httpPost(where: String, params: Object): Try[String] = {
  params match {
    // JavaScript string becomes Java's String:
    case body: String =>
      // ...

    // JavaScript object becomes Java's ScriptableObject
    case obj: ScriptableObject =>
      val params = convertToMap(obj)
      // ...
  }
}

protected def convertToMap(obj: ScriptableObject): Map[String, String] = {
  (for (key <- obj.getIds().toList) yield {
    (key.toString, obj.get(key) match {
      case s: String =>
        s
      case d: java.lang.Double if d.toString.endsWith(".0") =>
        d.toInt.toString
      case other => other.toString
    })
  }).toMap
}

The only way I found to access information stored in JavaScript objects is to look at them as an instance of sun.org.mozilla.javascript.ScriptableObject. Now this works like a charm on my local OpenJDK installation

java version "1.7.0_75"
OpenJDK Runtime Environment (fedora-2.5.4.2.fc20-x86_64 u75-b13)
OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode)

but when I run the same code on my Hadoop cluster, which is running

java version "1.7.0_67"
Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)

then I get:

java.lang.NoClassDefFoundError: sun/org/mozilla/javascript/ScriptableObject
    sun.org.mozilla.javascript.internal.JavaMembers.discoverAccessibleMethods(JavaMembers.java:383)
    sun.org.mozilla.javascript.internal.JavaMembers.discoverAccessibleMethods(JavaMembers.java:335)
    sun.org.mozilla.javascript.internal.JavaMembers.reflect(JavaMembers.java:455)
    sun.org.mozilla.javascript.internal.JavaMembers.<init>(JavaMembers.java:76)
    sun.org.mozilla.javascript.internal.JavaMembers.lookupClass(JavaMembers.java:847)
    sun.org.mozilla.javascript.internal.NativeJavaObject.initMembers(NativeJavaObject.java:88)
    sun.org.mozilla.javascript.internal.NativeJavaObject.<init>(NativeJavaObject.java:78)
    sun.org.mozilla.javascript.internal.NativeJavaObject.<init>(NativeJavaObject.java:68)
    ...

and looking at the version of Rhino that Oracle bundles with the JDK 7 as downloadable from http://www.oracle.com/technetwork/opensource/jdk7-source-1634015.html it seems like all sun.org.mozilla.javascript.* classes have been moved to sun.org.mozilla.javascript.internal.*.

Now how do I deal with that situation? Is there any Rhino-independent way of accessing the fields of a JavaScript object in Java? Or, how can I force the Oracle JVM to use the ...javascript.internal.ScriptableObject while using ...javascript.ScriptableObject in my local environment?

Any help much appreciated.

1
  • Hm. Just out of my mind: you are providing a DomainSpecificLanguage with Jacascript Syntax to your users.Did you take a look a look at JSR 233? Also converting a Javascript Object there are many Libraries that already cover the topic, my first hit was stackoverflow.com/questions/1395551/… Commented Mar 13, 2015 at 11:09

1 Answer 1

1

You can use function overloading instead.

// `params` matches a JavaScript string
def httpPost(where: String, params: String): Try[String] = {
  // ...
}

// `params` matches a JavaScript object
def httpPost(where: String, params: java.util.Map[_, _]): Try[String] = {
  // ...
}

This solution worked on my environment (Oracle JDK 8 and OpenJDK 1.7).

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much, that worked! Note that it must indeed be java.util.Map, not Scala's Map, even if the implicit conversions from scala.collection.JavaConversions are present.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.