Nashorn : how to evaluate scripts in scripting mode - nashorn

Im starting to explore jdk 8 new javascript engine nashorn and wanted to build some automating task scripts. I ve an issue, ive no idea how to evaluate a js file in scripting mode from javascript, using engine.eval() eg .
p.s: im not talking about jjs -scripting which is good but only works one way. I want the other way; make the engine evaluate in scripting mode from java

The easiest way is to add -Dnashorn.args=-scripting to you java command line.

After a lot of head scratching, i came up with a trick where i can actually launch my script's execution through a command line from a hand crafted System Process :
//tricking the nashorn engine with jjs command
public void evalScriptInScriptingMode(String fileName)
{
String[] args = new String[]{"jjs", "-scripting", fileName};
//This class is used to create operating system processes
ProcessBuilder pb = new ProcessBuilder(args);
pb.directory(null);
File log = new File("jjs_log.txt");
int i = 0;
while(log.exists())
{
i++;
log = new File("jjs" + i + "_log.txt");
}
pb.redirectErrorStream(true);
pb.redirectOutput(ProcessBuilder.Redirect.appendTo(log));
Process p = null;
try
{
p = pb.start(); //start the process which remains open
}
catch(IOException e)
{
e.printStackTrace();
}
}

You can pass arguments to the script engine via the NashornScriptEngineFactory.
import jdk.nashorn.api.scripting.NashornScriptEngineFactory
new NashornScriptEngineFactory()
.getScriptEngine( "-scripting" )
.eval( "" );

You can also use new NashornScriptEngineFactory().getScriptEngine("-scripting"); which will retrieve a new Nashorn ScriptEngine in scripting mode. This method is slightly better than using a System Process mainly because this automatically adds you classes to the nashorn classpath.
Basically, you can program classes in java and then use'em in javascript. If you do not need to be able to reference your classes in javascript then the System Process should do just fine and there won't be problems, ( if the machine on which this is running has jjs in their classpath )

Related

GraalVM + Nashorn Cannot extend classes

I'm attempting to use graal + nashorn interop to write nodejs that interacts with Java. I'm starting graal with node --jvm --jvm.Xss2m --jvm.Dtruffle.js.NashornJavaInterop=true --jvm.classpath=libs/ --polyglot app.js. I cannot extend a class though. The javascript code is
const GraaljsgrpcServiceImpl = Java.type('com.thing.GraaljsgrpcServiceImpl');
const HelloReply = Java.type('com.thing.HelloReply');
var GrpcImpl = Java.extend(GraaljsgrpcServiceImpl, {
sayHello: function(request, responseObserver) {
responseObserver.onNext(HelloReply.newBuilder().setMessage("Hello " + request.getName()).build());
responseObserver.onCompleted();
}
});
and my error is
TypeError: Could not determine a class loader with access to the JS engine and class com.thing.GraaljsgrpcServiceImpl This seems to point to a classloader issue with the java code I'm referencing and where the javascript is running(i.e. 2 different classloaders). Is there a way to pull this off when using graal/nodejs?
Thanks for reporting this classloader problem. We are looking into that and should be able to fix that with our next release (0.32 expected early March).
Regarding the --jvm.Dtruffle.js.NashornJavaInterop=true flag: we strongly encourage not using that, but depending on the built-in interoperability of GraalVM. However, in this mode Graal.js is not 100% compatible with Nashorn/Rhino; for instance, we are not supporting Java.extend in that mode. Is Java.extend the only reason why you set the NashornJavaInterop flag?
Thanks,
Christian

Java 8 Nashorn load script without executing it

I am using Java 8 Nashorn to execute a specific previously agreeed upon method. I could Invoke the specific method no problem. One thing that bugged me though is that when I load the script, it also executes it.
For example if file.js contains a print("hello world!") the scriptEngine.eval(new FileReader("./file.js") would execute and print hello world. I have to do this before I could invoke the specific method I want.
Is there a way to eval()/load the scripts without executing it?
Thanks
turns out You could do this by Casting engine to Compilable then call the compile function.
final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
final Compilable compilable = (Compilable) engine;
final Invocable invocable = (Invocable) engine;
final String statement = "function fetch(values) { return values['first_name'] + ' ' + values['last_name']; };";
final CompiledScript compiled = compilable.compile(statement);
This achieved what I want without needing to eval() it

How does one run Java 8's nashorn under a SecurityManager

I am looking to sandbox Java 8's Nashorn javascript engine. I've already discovered the --no-java flag, which helps, but I've also found the following link saying that one needs to be "running with SecurityManager enabled": http://mail.openjdk.java.net/pipermail/nashorn-dev/2013-September/002010.html
I haven't found documentation addressing how this is done with Nashorn, so how should this be done safely?
I know you probably don't need that anyway anymore, but for those who got here looking for an easy way to run nashorn in sandbox: if you just want to prevent scripts from using reflection, set up a ClassFilter. This way you can allow to use only SOME of the available classes... or none at all.
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine scriptEngine = factory.getScriptEngine(
new String[] { "--no-java" }, //a quick way to disable direct access to java API
null, //a ClassLoader, let's just ignore it
new ClassFilter() { //this one simply forbids use of any java classes, including reflection
#Override
public boolean exposeToScripts(String string) {
return false;
}
}
);
It is possible to execute scripts using jjs with security manager enabled.
jjs -J-Djava.security.manager myscript.js
or
jjs -J-Djava.security.manager
for interactive mode. Note that if you just use -Djava.security.manager, than that option is processed by jjs tool. To pass option to VM, you've to use -J prefix. This is true of any other JDK bin tool other than the launcher tool "java".
Unlike the java command, it doesn't seem possible to enable a security manager by setting the java.security.manager property on the jjs command line. (This might be a bug.) However, you can call the Java APIs from JavaScript to enable the security manager. In Java, this is
System.setSecurityManager(new SecurityManager());
and in JavaScript/Nashorn it's pretty much the same except you provide fully qualified class names:
java.lang.System.setSecurityManager(new java.lang.SecurityManager())
(Alternatively, you can import the names.) Either you can put this line into your application script, or you can put it into a script that you place on the jjs command line before your application script.
Example:
$ cat userhome.js
print(java.lang.System.getProperty("user.home"))
$ jjs userhome.js
/Users/xyzzy
$ cat secmgr.js
java.lang.System.setSecurityManager(new java.lang.SecurityManager())
$ jjs secmgr.js userhome.js
Exception in thread "main" java.security.AccessControlException: access denied ("java.util.PropertyPermission" "user.home" "read")
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:457)
[...snip...]
It does work to set the policy file on the command line, though:
$ cat all.policy
grant {
permission java.security.AllPermission;
};
$ jjs -Djava.security.policy=all.policy secmgr.js userhome.js
/Users/xyzzy
Or you can just add the equivalent setProperty call before enabling the security manager:
$ cat secmgr.js
java.lang.System.setProperty('java.security.policy', 'all.policy')
java.lang.System.setSecurityManager(new java.lang.SecurityManager())

Jenkins Groovy Postbuild use static file instead of script

Is it possible to load an external groovy script into the groovy post build plugin instead of pasting the script content into each job? We have approximately 200 jobs so updating them all is rather time consuming. I know that I could write a script to update the config files directly (as in this post: Add Jenkins Groovy Postbuild step to all jobs), but these jobs run 24x7 so finding a window when I can restart Jenkins or reload the config is problematic.
Thanks!
Just put the following in the "Groovy script:" field:
evaluate(new File("... groovy script file name ..."));
Also, you might want to go even further.
What if script name or path changes?
Using Template plugin you can create a single "template" job, define call to groovy script (above line) in there, and in all jobs that need it add post-build action called "Use publishers from another project" referencing this template project.
Update: This is what really solved it for me: https://issues.jenkins-ci.org/browse/JENKINS-21480
"I am able to do just that by doing the following. Enter these lines in lieu of the script in the "Groovy script" box:"
// Delegate to an external script
// The filename must match the class name
import JenkinsPostBuild
def postBuild = new JenkinsPostBuild(manager)
postBuild.run()
"Then in the "Additional groovy classpath" box enter the path to that file."
We do it in the following fashion.
We have a file c:\somepath\MyScriptLibClass.groovy (accessible for Jenkins) which contains code of a groovy class MyScriptLibClass. The class contains a number of functions designed to act like static methods (to be mixed in later).
We include this functions writing the following statement in the beginning of sytem groovy and postbuild groovy steps:
[ // include lib scripts
'MyScriptLibClass'
].each{ this.metaClass.mixin(new GroovyScriptEngine('c:\\somepath').loadScriptByName(it+'.groovy')) }
This could look a bit ugly but you need to write it only once for script. You could include more than one script and also use inheritance between library classes.
Here you see that all methods from the library class are mixed in the current script. So if your class looks like:
class MyScriptLibClass {
def setBuildName( String str ){
binding?.variables['manager'].build.displayName = str
}
}
in Groovy Postbuild you could write just:
[ // include lib scripts
'MyScriptLibClass'
].each{ this.metaClass.mixin(new GroovyScriptEngine('c:\\somepath').loadScriptByName(it+'.groovy')) }
setBuildName( 'My Greatest Build' )
and it will change your current build's name.
There are also other ways to load external groovy classes and it is not necessary to use mixing in. For instance you can take a look here Compiling and using Groovy classes from Java at runtime?
How did I solve this:
Create file $JENKINS_HOME/scripts/PostbuildActions.groovy with following content:
public class PostbuildActions {
void setBuildName(Object manager, String str ){
binding?.variables['manager'].build.displayName = str
}
}
In this case in Groovy Postbuild you could write:
File script = new File("${manager.envVars['JENKINS_HOME']}/scripts/PostbuildActions.groovy")
Object actions = new GroovyClassLoader(getClass().getClassLoader()).parseClass(script).newInstance();
actions.setBuildName(manager, 'My Greatest Build');
If you wish to have the Groovy script in your Code Repository, and loaded onto the Build / Test Slave in the workspace, then you need to be aware that Groovy Postbuild runs on the Master.
For us, the master is a Unix Server, while the Build/Test Slaves are Windows PCs on the local network. As a result, prior to using the script, we must open a channel from the master to the Slave, and use a FilePath to the file.
The following worked for us:
// Get an Instance of the Build object, and from there
// the channel from the Master to the Workspace
build = Thread.currentThread().executable
channel = build.workspace.channel;
// Open a FilePath to the script
fp = new FilePath(channel, build.workspace.toString() + "<relative path to the script in Unix notation>")
// Some have suggested that the "Not NULL" check is redundant
// I've kept it for completeness
if(fp != null)
{
// 'Evaluate' requires a string, so read the file contents to a String
script = fp.readToString();
// Execute the script
evaluate(script);
}
I've just faced with the same task and tried to use #Blaskovicz approach.
Unfortunately it does not work for me, but I find upgraded code here (Zach Auclair)
Publish here with minor changes:
postbuild task
//imports
import hudson.model.*
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import java.io.File;
// define git file
def postBuildFile = manager.build.getEnvVars()["WORKSPACE"] + "/Jenkins/SimpleTaskPostBuildReporter.GROOVY"
def file = new File(postBuildFile)
// load custom class from file
Class groovy = this.class.classLoader.parseClass(file);
// create custom object
GroovyObject groovyObj = (GroovyObject) groovy.newInstance(manager);
// do report
groovyObj.report();
postbuild class file in git repo (SimpleTaskPostBuildReporter.GROOVY)
class SimpleTaskPostBuildReporter {
def manager
public SimpleTaskPostBuildReporter(Object manager){
if(manager == null) {
throw new RuntimeException("Manager object can't be null")
}
this.manager = manager
}
public def report() {
// do work with manager object
}
}
I haven't tried this exactly.
You could try the Jenkins Job DSL plugin which allows you to rebuild jobs from within jenkins using a Groovy DSL and supports post build groovy steps directly from the wiki
Groovy Postbuild
Executes Groovy scripts after a build.
groovyPostBuild(String script, Behavior behavior = Behavior.DoNothing)
Arguments:
script The Groovy script to execute after the build. See the plugin's
page for details on what can be done. behavior optional. If the script
fails, allows you to set mark the build as failed, unstable, or do
nothing. The behavior argument uses an enum, which currently has three
values: DoNothing, MarkUnstable, and MarkFailed.
Examples:
This example will run a groovy script that prints hello, world and if
that fails, it won't affect the build's status:
groovyPostBuild('println "hello, world"') This example will run a
groovy script, and if that fails will mark the build as failed:
groovyPostBuild('// some groovy script', Behavior.MarkFailed) This example
will run a groovy script, and if that fails will mark the
build as unstable:
groovyPostBuild('// some groovy script', Behavior.MarkUnstable) (Since 1.19)
There is a facility to use a template job (this is the bit I haven't tried) which could be the job itself so you only need to add the post build step. If you don't use a template you need to recode the whole project.
My approach is to have a script to regenerate or create all jobs from scratch just so I don't have to apply the same upgrade multiple times. Regenerated jobs keep their build history
I was able to get the following to work (I also posted on this jira issue).
in my postbuild task
this.class.classLoader.parseClass("/home/jenkins/GitlabPostbuildReporter.groovy")
GitlabPostbuildReporter.newInstance(manager).report()
in my file on disk at /home/jenkins/GitlabPostbuildReporter.groovy
class GitlabPostbuildReporter {
def manager
public GitlabPostbuildReporter(manager){
if(manager == null) {
throw new RuntimeException("Manager object musn't be null")
}
this.manager = manager
}
public def report() {
// do work with manager object
}
}

Windows Shell Extension dll and Winform process

I made a dll for Windows Shell Extension integration, following this tutorial http://blogs.msdn.com/b/codefx/archive/2010/09/14/writing-windows-shell-extension-with-net-framework-4-c-vb-net-part-1.aspx[^]
Now, I added a Windows form in that dll, I'm doing the following:
void OnVerbDisplayFileName(IntPtr hWnd)
{
ShowSelectedFiles form = new ShowSelectedFiles();
form.Show(selectedFiles);
}
Everything works fine, just the Forms icon is not shown in task bar and I can't find the process that runs my form.
Any tip on how to solve this problem? Maybe by starting a new process and then showing the form?
Thanks
Try using the Form.Show Method (IWin32Window) method so that you can specify the owner window.
See http://ryanfarley.com/blog/archive/2004/03/23/465.aspx for how to specify the owner window from an hWnd.
Also make sure that the form's ShowInTaskBar property is true.
The only way to solve this is to create another process.
void OnVerbDisplayFileName(IntPtr hWnd)
{
string file = (new System.Uri(Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath;
string executableName = file.Substring(0, file.LastIndexOf("/"));
executableName += "/MyApp.exe";
Process gui = new Process();
gui.StartInfo.FileName = executableName;
gui.StartInfo.Arguments = selectedFiles.JoinFileNames(" ");
gui.Start();
}
Cheers!

Resources