Is it to possible to store closures from different groovy scripts?
Let's say I have some kind of class that should store the closures:
package com.test
class ClosureContainer {
static closures = [:]
static def AddClosure(String name, Closure cl) {
println "Adding closure named ${name}"
closures[name] = cl
}
}
And then I would like to have groovy scripts that would add closures to it:
import com.test.ClosureContainer as container
container.AddClosure("Interesting stuff", {
println "I'm doing some interesting stuff"
})
And later, I should be able to call it like:
def closureCode = ClosureContainer.closures["Interesting stuff"]
closureCode()
What is the best approach to do it in Groovy? I'm not sure how to handle invoking of the scripts because classes are generated from these scripts.
I'm able to create instances of the scripts during runtime, but I'm not able to retrieve list of the classes/scripts without hardcoding it.
UPDATE:
Let's suppose I have testScript.groovy in package com.test.scripts that adds a few closures to the container. I tried to let the gradle generate classes from the scripts and create instance in the code like this:
def className = Class.forName("com.test.scripts.testScript")
def instance = className.newInstance()
instance.run()
And I'm hardcoding the testScript name. But there will be a lot of scripts and I should be able to retrieve it dynamically.
The recommended way to run groovy script is to use GroovyScriptEngine:
String[] path = new String[] { "." };
GroovyScriptEngine engine = new GroovyScriptEngine(path);
engine.run("yoursriptname.groovy", new Binding())
NB: yoursriptname should be the path of your script relative to path.
If you want to pass bindings (arguments and then get the results) you have to use Binding.
Related
I need to import custom classes defined outside of my jenkinsfile. These classes have constructors which require parameters. I've already tried using load() for each of the class files, but it seems this only works for script files that just define static methods. When I tried using this to load my class files, it threw an error that it could not find an <init> method.
I currently have a way of importing these classes that works, but it requires re-cloning the repository as a library (code below). I'd like to move away from this since it's inefficient and seems like it should be unnecessary.
current working (but not great) implementation:
Jenkinsfile:
lib = library(identifier: "<libraryName>#${env.currentBranch}",
retriever: modernSCM([$class: 'GitSCMSource',
credentialsId: <credentialsId>,
id: '<id>',
remote: env.projectRemote,
traits: [[$class: 'jenkins.plugins.git.traits.BranchDiscoveryTrait']]])).com.company.jenkins.sdk
def git = lib.Git.new(this, currentStage, currentStep)
...
src/com/company/jenkins/sdk/Git.groovy:
package com.company.jenkins.sdk.Git
class Git implements Serializable {
def script
def stage
def step
Git(script, stage, step){
...
}
}
I'd like to get rid of that library command and replace it with a simpler way of importing these classes.
I was working on injecting of groovy scripts dynamically in Java. So before executing those scripts, I want to get sure of that they do not have potential bugs using SpotBugs (static code analyzer).
Here is the Psuedo-Code:
Here it should return the infinite loop bug.
String script = "class Hello { static void main(String []args) { def i = 0; while ( i <= 0) { i = i - 1; } } } ";
List<Bugs> bugs = SpotBugs.getBugs(script);
if (bugs == null) {
execute(script);
}
So how to do the SpotBugs.getBugs(script) using java, the input script will not be hard-coded as in above example, but will be dynamically fetched.
Easiest API
The easiest way is to write the compiled code to class files (in a temp directory if needed). By having compiled class as file, you will be able to use the FindBugs class which provide an API to configure the scope and rules without playing with internal classes that are subject to changes.
Groovy dynamic (default) vs static compiling
However, the main obstacle you'll face is that groovy bytecode is too obfuscated for SpotBugs. For the call to function abc(), you will not see an invoke to method abc in the bytecode. It will be a reference to a global functions map that is created at runtime. Groovy has a mode to compile to a less dynamic format. This mode does not allow functions to be created at runtime. You can check the configuration to instruct the compiler for the static mode in this test repo: https://github.com/find-sec-bugs/find-sec-bugs-demos/tree/master/groovy-simple. This is, however, a Gradle compilation not a programmatic API that received a String as code.
seems like SpotBugs should run using maven, which means it will package and include only the groovy scripts that are valid.
hence, you will not need to check before execution.
I'm new to using Command Line Interface. So I just have a question on how to invoke the runner class of the cucumber using CLI technique.
I have a Java program which contains a main method. When testers pass the argument which is test case, it will fetch the feature file. The java program invoke a custom made API which will fetch the correct feature file.
Next I'll have to invoke the Cucumber runner class to execute the test case. I need to pass this particular feature file as the argument. Two questions, Can we invoke the runner class from a different main method. I did some research and I was not able to find a concrete answer.
Two questions,
cucumber.api.cli.Main.main(arguments); So how do i specify the jar location of my runner class.
`FeatureFileCreation.main("xxxxx"); - API that fetches the right feature file
String[] arguments = {"-", ""};
cucumber.api.cli.Main.main(arguments);
How do I specify where my jar is located? How can I pass my feature file?`
Should I create a main method in the runner class, something like this? For the sake of using CLI,Since I need to create a runnable jar. I should have a main method in my runner class.
`
#RunWith(Cucumber.class)
#Cucumber.Options(features="C:/Users/IBM_ADMIN/Desktop/CRAutomation/CR Regression1/src/My.feature",glue={"bell.canada.step.definition"})
public class AutomationRunnerAction {
public void main(){
}
}`
Please note that, Getting the right feature file is 1 java API. I will invoking that API from one main method of one java program. The runner class with step definition and methods are a diff java program.
Unfortunately the accept answer is not correct. If you look at the source of Main.main() you'll notice that it contains: System.exit(exitstatus) which terminates the system.
The proper way to run the commandline programatically would be to use Main.run() like this:
String [] argv = new String[]{ "-g","","./src/main/java/featureDetails/Testing.feature"};
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
byte exitstatus = Main.run(argv, contextClassLoader);
Try this if this works. You do not need any Runner class. Just call the static main method of Main class that corresponds to running cucumber from cli.
public static void main(String[] args) throws Throwable {
//Your code to get feature file full path
Main.main(new String[]{"-g", "classpath to step definition file", "Full path to feature file"});
// My stepdefinition is inside java package at cucumber.sample.test
// My feature file is inside src/test/resources/features/samplethree.feature
}
For additional parameters like tags or plugin use "-t","#Tags". Important the feature file path has to be the last option.
I am running this for Eclipse with Maven setting up classpath and jar dependencies.
In a custom plugin (or task) I would like to read all compiled classes (preferrably those that have changed from last compilation) with a classloader so that I'll be able to use reflection on them.
Is that possible?
1) It would be great to have a cook right after a Java class was compiled so that I could read it, but I found no way to do this.
2) I'm thinking of something like this ...
compileJava.doLast {
ClassLoader parent = getClass().getClassLoader();
GroovyClassLoader loader = new GroovyClassLoader(parent);
// retrieve all class files
// for each class file, loader.parseClass(classFile)
}
In a gradle script getClass().getClassloader() will get you the classloader of the gradle script. This will NOT contain the compiled classes or compile/runtime jars. I think you want to do something similar to:
Collection<URL> urls = sourceSets.main.runtimeClasspath.files.collect { it.toURI().toURL() }
Classloader parent = new URLClassLoader(urls.toArray());
If you want to only act on the classes that have changed you are best to do do that in an incremental task
I have built a jar from my Scala project.
I have the following structure for what I want to use from this jar
package aaa.bbb.ccc
case class FooResult(...)
trait Foo(...) {
def bar(): FooResult
}
object Foo {
private class FooImpl(...) extends Foo {
...
}
def apply(...): Foo
}
First question: Maybe I have misunderstood something in what Py4J offers,
but do I have to write a java/scala class to start the Py4J gateway if I want to use my own classes? Or is it enough to add it to the gateway's jvm's classpath?
Second question (which I guess doesn't apply depending on the answer to above): How do I add my jar when starting the java gateway in order to make it available? To solve this temporarily, I just started the jvm manually with my jar along with the Py4J jar with this command
java -classpath "path/to/py4j.jar:path/to/my.jar" py4j.GatewayServer 0
and then connected to it manually from the Python code. Then I tried to import my classes via
java_import(gateway.jvm, "aaa.bbb.ccc.*")
which didn't throw any error but I'm not sure it worked because it doesn't throw any error if I input some fake classpath.
Third question (which applies if the answer to the first is that I have to write the entry point to access my classes): How does this work when using scala?
object Main extends App {
val gw = new GatewayServer(// TODO: how to expose my classes here?
}