How do I set up jsr223 scripting with scala as scripting language - java

So far I have tried the sling implementation for jsr223 scripting for scala, but was not able to get it set up correctly.
when I do this:
public static void main(String[] args) {
try {
new ScriptEngineManager().getEngineByName("scala").
eval("object HelloWorld {def main(args: Array[String]) {
println(\"Hello, world!\") }}");
} catch (ScriptException e) {
e.printStackTrace();
}
}
I got nothing but:
javax.script.ScriptException: ERROR
org.apache.sling.scripting.scala.Script line 13 : not found: type
Script at org.apache.sling.scripting.scala.ScalaScriptEngine.eval(ScalaScriptEngine.scala:117)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:247)
similar Problems are discussed here:
http://scala-programming-language.1934581.n4.nabble.com/How-to-compile-Scala-code-from-java-using-the-current-ClassLoader-instead-of-a-string-based-classpat-td1955873.html#a1955873
and
http://dev.day.com/discussion-groups/content/lists/sling-dev/2009-12/2009-12-01_Scala_scripting_support_was_Re_And_another_one____Michael_D_rig.html
maybe there is another Implementation that I'm not aware of.
Any help appreciated

Have a look at the test cases in the scala/script module of Apache Sling for a working example. The script and its entry point (that is the object) need to follow certain conventions. I'll provide more information on these if required later.
For a general overview of the scripting engine see my session slides from Scala Days 2010.
Update: Scripts must be of the following form:
package my.cool.script {
class foo(args: fooArgs) {
import args._ // import the bindings
println("bar:" + bar)
}
}
The type of args is generated by the script engine and is named after the simple class name of the script appended with 'Args'. Further the example assumes, that the Bindings passed for script evaluation contains a value for the name 'bar'. For further details see the class comment on ScalaScriptEngine.
You need to pass the name of your script class to the script engine. You do this by putting the fully qualified script name (i.e. my.cool.script.foo) into the ScriptContext by the name 'scala.script.class'.

With the conclusion of https://issues.scala-lang.org/browse/SI-874 in version 2.11, it should be as easy as what is shown in the ticket:
import javax.script.*;
ScriptEngine e = new ScriptEngineManager().getEngineByName("scala");
e.getContext().setAttribute("label", new Integer(4), ScriptContext.ENGINE_SCOPE);
try {
engine.eval("println(2+label)");
} catch (ScriptException ex) {
ex.printStackTrace();
}

Unfortunately my comment was unreadable without linebreaks - so...
To be able to run the Codesnippet mentioned I needed to make the following changes.
I used Scala 2.11.0-M4
public static void main(String args[]){
ScriptEngine engine = new ScriptEngineManager().getEngineByName("scala");
// Set up Scriptenvironment to use the Java classpath
List nil = Nil$.MODULE$;
$colon$colon vals = $colon$colon$.MODULE$.apply((String) "true", nil);
((IMain)engine).settings().usejavacp().tryToSet(vals);ScriptContext.ENGINE_SCOPE);
engine.getContext().setAttribute("labelO", new Integer(4), ScriptContext.ENGINE_SCOPE);
try {
engine.eval("val label = labelO.asInstanceOf[Integer]\n"+
"println(\"ergebnis: \" + (2 + label ))");
} catch (ScriptException ex) {
ex.printStackTrace();
}
}

Related

Automatically handling / ignoring NameError in Jython

I have a setup where I execute jython scripts from a Java application. The java application feed the jython script with variables, coming from the command line, so that a user can write the following code in it's jython script:
print("Hello, %s" % foobar)
And will call the java program with this:
$ java -jar myengine.jar script.py --foobar=baz
Hello, baz
My java application parse the command-line, and create a variable of that name with the given value to give to the jython scripting environment to consume. All is well so far.
My issue is that when the user does not provide the foobar command-line parameter, I'd like to be able to easily provide a fallback in my script. For now, the user needs to write that sort of code to handle the situation where the foobar parameter is missing from the command-line:
try: foobar
except NameError: foobar = "some default value"
But this is cumbersome, especially if the number of parameters is growing. Is there a way to handle that better from the script user point of view?
I was thinking of catching the jython NameError in the Java code, initializing the variable causing the exception to a default value if the variable causing the exception "looks like" a parameter (adding a naming convention is OK), and restarting where the exception occurred. Alternatively, I can require the script user to write code such as this:
parameter(foobar, "some default value")
Or something equivalent.
Well, this is one ugly workaround I found so far. Be careful, as this will call the script in loop many times, and is O(n^2).
private void callScriptLoop(String scriptfile) {
PythonInterpreter pi = new PythonInterpreter();
pi.set("env", someEnv);
int nloop = 0;
boolean shouldRestart;
do {
shouldRestart = false;
try {
pi.execfile(scriptfile);
} catch (Throwable e) {
if (e instanceof PyException) {
PyException pe = (PyException) e;
String typ = pe.type.toString();
String val = pe.value.toString();
Matcher m = Pattern.compile("^name '(.*)' is not defined")
.matcher(val);
if (typ.equals("<type 'exceptions.NameError'>")
&& m.find()) {
String varname = m.group(1);
pi.set(varname, Py.None);
System.out.println(
"Initializing missing parameter '"
+ varname + "' to default value (None).");
shouldRestart = true;
nloop++;
if (nloop > 100)
throw new RuntimeException(
"NameError handler infinite loop detected: bailing-out.");
}
}
if (!shouldRestart)
throw e;
}
} while (shouldRestart);
}

Nashorn Abstract Syntax Tree Traversal

I am attempting to parse this Javascript via Nashorn:
function someFunction() { return b + 1 };
and navigate to all of the statements. This including statements inside the function.
The code below just prints:
"function {U%}someFunction = [] function {U%}someFunction()"
How do I "get inside" the function node to it's body "return b + 1"? I presume I need to traverse the tree with a visitor and get the child node?
I have been following the second answer to the following question:
Javascript parser for Java
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.options.Options;
import java.util.List;
public class Main {
public static void main(String[] args){
Options options = new Options("nashorn");
options.set("anon.functions", true);
options.set("parse.only", true);
options.set("scripting", true);
ErrorManager errors = new ErrorManager();
Context context = new Context(options, errors, Thread.currentThread().getContextClassLoader());
Source source = Source.sourceFor("test", "function someFunction() { return b + 1; } ");
Parser parser = new Parser(context.getEnv(), source, errors);
FunctionNode functionNode = parser.parse();
Block block = functionNode.getBody();
List<Statement> statements = block.getStatements();
for(Statement statement: statements){
System.out.println(statement);
}
}
}
Using private/internal implementation classes of nashorn engine is not a good idea. With security manager on, you'll get access exception. With jdk9 and beyond, you'll get module access error w/without security manager (as jdk.nashorn.internal.* packages not exported from nashorn module).
You've two options to parse javascript using nashorn:
Nashorn parser API ->https://docs.oracle.com/javase/9/docs/api/jdk/nashorn/api/tree/Parser.html
To use Parser API, you need to use jdk9+.
For jdk8, you can use parser.js
load("nashorn:parser.js");
and call "parse" function from script. This function returns a JSON object that represents AST of the script parsed.
See this sample: http://hg.openjdk.java.net/jdk8u/jdk8u-dev/nashorn/file/a6d0aec77286/samples/astviewer.js

how to get details of the pl sql package after parsing in java

I have a pkb file. It contain a package and under that package it has multiple functions.
I have to get the following details out of it:
package name
function names (for all functions one by one)
params in function
return type of function
Approach: I am parsing the pkb file. I have taken the grammar from these sources:
Presto
Antlrv4 Grammer for plsql
After getting these grammar I downloaded the jar from antlr-4.5.3-complete.jar. Then using
java -cp org.antlr.v4.Tool grammar.g
one by one I execute this command on these grammars separately to generate listener, lexer, parser and other files.
After this I created two project in eclipse one for each grammar. I imported these generated file into the respective and set antlr-4.5.3-complete.jar file into the path. After this I used following code to check if my .pkb file is correct or not?
public static void parse(String file) {
try {
SqlBaseLexer lex = new SqlBaseLexer(new org.antlr.v4.runtime.ANTLRInputStream(file));
CommonTokenStream tokens = new CommonTokenStream(lex);
SqlBaseParser parser = new SqlBaseParser(tokens);
System.err.println(parser.getNumberOfSyntaxErrors()+" Errors");
} catch (RecognitionException e) {
System.err.println(e.toString());
} catch (java.lang.OutOfMemoryError e) {
System.err.println(file + ":");
System.err.println(e.toString());
} catch (java.lang.ArrayIndexOutOfBoundsException e) {
System.err.println(file + ":");
System.err.println(e.toString());
}
}
I am not getting any error in parsing the file.
But after this I am stuck with next steps. I need to get all the package name, functions, params etc.
How to get these details?
Also is my approach is correct to attain the required output.
The Presto grammar is a generic SQL grammar which is not suitable for parsing Oracle packages. The ANTLRv4 grammar for PL/SQL is the right tool for your task.
Generally an ANTLR grammar as such works as a validator. When you want to make some additional processing while parsing you should use ANTLR actions (see overview slide in this presentation). These are blocks of text written in the target language (e.g. Java) and enclosed in curly braces (see documentation).
There are at least two ways to solve your task with ANTLR actions.
Stdout output
The simplest way is to add println()s for certain rules.
To print package name modify package_body rule in plsql.g4 as follows:
package_body
: BODY package_name (IS | AS) package_obj_body*
(BEGIN seq_of_statements | END package_name?)
{System.out.println("Package name is "+$package_name.text);}
;
Similarly to print information about function's arguments and return type: add prinln()s in create_function_body rule. But there is an issue whith printing of parameters. If you use $parameter.text it will return name, type specification and default value according to parameter rule without spaces (as token sequence). If you add println() to parameter rule and use $parameter_name.text it will print all parameter's names (including parameters of procedures, not only functions). So you can add an ANTLR return value for parameter rule and assign $parameter_name.text to the return value:
parameter returns [String p_name]
: parameter_name (IN | OUT | INOUT | NOCOPY)*
type_spec? default_value_part?
{$p_name=$parameter_name.text;}
;
Thus is context of create_function_body we can access the parameter's name by $parameter.p_name:
create_function_body
: (CREATE (OR REPLACE)?)? FUNCTION function_name
{System.out.println("Parameters of function "+$function_name.text+":");}
('(' parameter {System.out.println($parameter.p_name);}
(',' parameter {System.out.println($parameter.p_name);})* ')')?
RETURN type_spec
(invoker_rights_clause|parallel_enable_clause|result_cache_clause|DETERMINISTIC)*
((PIPELINED? (IS | AS) (DECLARE? declare_spec* body | call_spec))
| (PIPELINED | AGGREGATE) USING implementation_type_name) ';'
{System.out.println("Return type of function "
+$function_name.text+" is "
+ $type_spec.text);}
;
Accumulation
Also you can save some calculations to variables and access them as parser class members. E.g. you can accumulate function's name in variable func_name. For this add #members section at beginning of the grammar:
grammar plsql;
#members{
String func_name = "";
}
And modify function_name rule as follows:
function_name
: id ('.' id_expression)? {func_name = func_name+$id.text + " ";}
;
Using lexer and parser classes
Here is an example of application to run your parser parse.java:
import org.antlr.v4.runtime.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class parse {
static String readFile(String path) throws IOException
{
byte[] encoded = Files.readAllBytes(Paths.get(path));
return new String(encoded, "UTF-8");
}
public static void main(String[] args) throws Exception {
// create input stream `in`
ANTLRInputStream in = new ANTLRInputStream( readFile(args[0]) );
// create lexer `lex` with `in` at input
plsqlLexer lex = new plsqlLexer(in);
// create token stream `tokens` with `lex` at input
CommonTokenStream tokens = new CommonTokenStream(lex);
// create parser with `tokens` at input
plsqlParser parser = new plsqlParser(tokens);
// call start rule of parser
parser.sql_script();
// print func_name
System.out.println("Function names: "+parser.func_name);
}
}
Compile and run
After this generate java code by ANTLR:
java org.antlr.v4.Tool plsql.g4
and compile your Java code:
javac plsqlLexer.java plsqlParser.java plsqlListener.java parse.java
then run it for some .pkb file:
java parse green_tools.pkb
You can find modified parse.java, plsql.g4 and green_tools.pkb here.

How to share a common bindings for different ScriptContext with Java ScriptEngine API?

I am playing with Nashorn and the ScriptEngine API of java 8. I have a unique instance of ScriptEngine to execute several different scripts. I would like to remove some functions (e.g. print) for all scripts I will execute, but specify a specific binding binding for each script.
My problem is that I do not know if it is possible to make a kind of hierarchy with ScriptContext.
Here is an exemple of what I would do :
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.remove("print");
// script I will run
String script1 = "print('Hello world')";
String script2 = "print(person)";
// define a different script context
ScriptContext newContext = new SimpleScriptContext();
Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
// set the variable to a different value in another scope
engineScope.put("person", "toto");
// evaluate script but in global context
// should throw message because print is not declared in the context
try {
engine.eval(script1);
} catch (ScriptException se) {
System.out.println(se.getMessage()); // out : ReferenceError: "print" is not defined in <eval> at line number 1
}
// should throw message because print is not declared in the context
engine.eval(script2, newContext); // out : toto
}
Here, the probeme is that when I execute script2 I would like that print has been removed from the context.
Is there a clean way to do that ?

How to force renaming of variable names in eclipse mars

How can I force eclipse mars to rename variable names?
When I try, I get
This refactoring cannot be performed correctly due to syntax errors in
the compilation unit.
The dialog only offers "Cancel".
It was possible to do this in older versions of eclipse, and I used the feature extensively, for example after copy&paste of code snippets found on the net.
Note this is not a duplicate of Refactoring variable names in Eclipse .
Edit 3 (summary of what happened):
In the code (shown below) were not only those common errors like missing imports or undeclared variables, but also a missing ";", thus a true syntax error. This, at first hidden among several other compiling issues, caused eclipse to refuse the refactoring.
As it turned out, this is not a special feature of mars but also of older versions of eclipse.
Edit: here comes my example code. It is mainly based on the examples from tutorialspoint for mongodb but very probably doesn't have anything to do with mongo.
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoCredential;
import com.mongodb.client.MongoDatabase;
public class MongoDBJDBC2 {
private static String myUserName;
private static String myPassword;
private static String myHost = "localhost";
private static String myDatabaseName = "mydb";
private static MongoDatabase db;
public MongoDBJDBC2() {
initDb();
// TODO Auto-generated constructor stub
}
public static void main(String args[]) {
MongoDBJDBC2 mo = new MongoDBJDBC2();
}
private static void initDb() {
MongoClientURI uri = new MongoClientURI(
"mongodb://" + myUserName + ":" + myPassword + "#" + myHost + "/?authSource=db1");
try (MongoClient mongoClient = new MongoClient(uri);) {
db = mongoClient.getDatabase(myDatabaseName);
System.out.println("Connect to database successfully");
// boolean auth = db.authenticate(myUserName, myPassword);
} catch (Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
}
}
public static void main4( String args[] ) {
try{
// To connect to mongodb server
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
// Now connect to your databases
DB db = mongoClient.getDB( "test" );
System.out.println("Connect to database successfully");
boolean auth = db.authenticate(myUserName, myPassword);
System.out.println("Authentication: "+auth);
DBCollection coll = db.getCollection("mycol");
System.out.println("Collection mycol selected successfully");
DBCursor cursor = coll.find();
while (cursor.hasNext()) {
DBObject updateDocument = cursor.next();
updateDocument.put("likes","200")
col1.update(updateDocument);
}
System.out.println("Document updated successfully");
cursor = coll.find();
int i = 1;
while (cursor.hasNext()) {
System.out.println("Updated Document: "+i);
System.out.println(cursor.next());
i++;
}
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
}
I try to rename db to myDb in
private static MongoDatabase db;
Previously I used eclipse Helios and never encountered this kind of "feature".
Edit2: I have located the fatal error. In method "main4" a semicolon is missing after
updateDocument.put("likes", "200")
Still don't understand why this upsets eclipse so much that it refuses to refactor, and I still would like to know if there is a way to force refactoring despite of errors.
Compilers issue two kinds of errors: syntax errors and all other kinds of errors, like "type mismatch" and "symbol not found". Eclipse complains about a syntax error. Are you sure that in previous occasions when Eclipse agreed to refactor your code despite the fact that it contained errors, it was syntax errors that your code contained? You see, there is a big difference.
Refactoring symbol names in java is far more involved than a simple text search and replace, the structure of your code has to be taken into account.
But in the case of a syntax error, the compiler has given up parsing your file, so it does not know the structure of your code: it does not know which tokens are variables, which tokens are types, which tokens are methods, etc. so it really cannot do the refactoring that you want.
So, if you must really proceed with your refactoring despite having syntax errors, then I am afraid that text search and replace is the way to go for you in this particular case.
But fixing the syntax errors before attempting to refactor would be the most prudent thing to do.
This happens when there is compilation issue in your code.
Fix the compilation issue than you can refactor your code.Yes this feature is recently introduced in newer version of eclipse.

Categories

Resources