groovy defualt imports
Is it possible to have our own package in default imports? Is there any way to tell the groovy runtime to use my own package as default import?
This JIRA covers you question.
The bulk of it is here.
class DefaultImportSourceUnitOperation extends SourceUnitOperation {
public void call(SourceUnit source) throws CompilationFailedException {
source.getAST().addImportPackage("pkg1.pgk2.pkg3.");
}
}
class DefaultImportClassLoader extends GroovyClassLoader {
protected CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource
codeSource) {
CompilationUnit cu = super.createCompilationUnit(config, codeSource)
cu.addPhaseOperation(new DefaultImportSourceUnitOperation(), Phases.CONVERSION)
return cu
}
}
Be sure not to forget to add the . to the end of the package declaration.
Goodluck!
Since Groovy 1.8 there is an easier way with ImportCustomizer:
def importCustomizer = new ImportCustomizer()
importCustomizer.addImports('com.looneytunes.Coyote', 'com.looneytunes.RoadRunner')
importCustomizer.addStarImports('com.acme')
importCustomizer.addStaticStars('com.looneytunes.Physics')
def configuration = new CompilerConfiguration()
configuration.addCompilationCustomizers(importCustomizer)
def shell = new GroovyShell(configuration)
shell.evaluate """
new Coyote().startPlanToCatch(new RoadRunner()) // no need for explicit imports
.useContraption(new PortableHole()) // from com.acme
.withPhysicsViolation({ forgetGravity(it) }) // method from com.looneytunes.Physics
.start()
"""
Related
I have a few classes which I'd like to keep as POJO. Manually annotating each of these would be troublesome, for both updating all current ones and adding future such classes.
I have a SourceAwareCustomizer able to identify all these classes. However I do not know how to apply the #POJO via the config script.
I tried ast(POJO), and I would get an error:
Provided class doesn't look like an AST #interface
I dug in the code a bit and found that #POJO is not an AST transformation (it's not annotated with #GroovyASTTransformationClass.
Is there a way to apply #POJO, or maybe a random annotation, to a class via the config script?
POJO is not an AST transformation.
Compare POJO source to ToString (for example). In POJO the GroovyASTTransformationClass annotation is missing..
I can't make #POJO working without #CompileStatic..
So, here is my try with groovy 4.0.1:
congig.groovy
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.AnnotationNode
import groovy.transform.stc.POJO
import groovy.transform.CompileStatic
withConfig(configuration) {
inline(phase:'SEMANTIC_ANALYSIS'){Object...args->
if(args.size()>2){
ClassNode cn = args[2]
if( cn.getSuperClass().name=="java.lang.Object" ) {
if( !cn.annotations.find{it.classNode.name==POJO.class.name} ) {
cn.addAnnotation( new AnnotationNode(new ClassNode(POJO.class)) )
//can't see how POJO could work without compile static in groovy 4.0.1
if( !cn.annotations.find{it.classNode.name==CompileStatic.class.name} )
cn.addAnnotation( new AnnotationNode(new ClassNode(CompileStatic.class)) )
}
}
println "class = $cn"
println "annotations = ${cn.getAnnotations()}"
}
}
}
A.groovy
class A{
String id
}
compile command line:
groovyc --configscript config.groovy A.groovy
generated class
public class A
{
private String id;
#Generated
public A() {}
#Generated
public String getId() {
return this.id;
}
#Generated
public void setId(final String id) {
this.id = id;
}
}
I use this tutorial to connect to Gmail API: https://developers.google.com/gmail/api/quickstart/java
I would like to make a keyword in Katalon Studio, which depends on Gmail API.
I modified from sample code that line:
InputStream in = GmailQuickstart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
to this:
InputStream ins = new FileInputStream(CREDENTIALS_FILE_PATH);
JAR files are added, project is running and browser window is opened to get token. After successful authorization I got error message:
Caused by: java.lang.NoSuchMethodError:
com.google.api.client.http.HttpRequest.setResponseReturnRawInputStream(Z)Lcom/google/api/client/http/HttpRequest;
UPDATE: List of imported dependencies:
commons-codec-1.15.jar
commons-logging-1.2.jar
google-api-client-1.31.3.jar
google-api-client-extensions-1.6.0-beta.jar
google-api-client-jackson2-1.31.3.jar
google-api-client-java6-1.31.3.jar
google-api-services-gmail-v1-rev110-1.25.0.jar
google-http-client-1.39.1.jar
google-http-client-jackson2-1.39.1.jar
google-oauth-client-java6-1.31.4.jar
google-oauth-client-jetty-1.31.4.jar
guava-30.1.1-jre.jar
httpclient-4.5.13.jar
httpcore-4.4.14.jar
j2objc-annotations-1.3.jar
jackson-core-2.12.2.jar
jsr305-3.0.2.jar
https://docs.katalon.com/katalon-studio/docs/external-libraries.html#exclude-built-in-libraries
With the ability to remove built-in libraries stored in the .classpath file of a project folder, you can replace a built-in library with an external one for flexible libraries usage in a test project.
Requirements
An active Katalon Studio Enterprise license.
Katalon Studio version 7.8.
UPD:
i got katalon 7.9.1 and here how i was able to do it:
add the following class into KS project:
include/scripts/groovy/(default package)/GroovyBox.java
import groovy.lang.*;
import java.util.regex.Pattern;
import java.util.Map;
import java.util.List;
/** run groovy script in isolated classloader*/
public class GroovyBox {
GroovyShell gs;
public GroovyBox(ClassLoader parentCL, Pattern excludeClassPattern ) {
FilteredCL fcl = new FilteredCL(parentCL, excludeClassPattern);
gs = new GroovyShell(fcl);
}
public GroovyBox withClassPath(List<String> classPathList) {
GroovyClassLoader cl = gs.getClassLoader();
for(String cp: classPathList) cl.addClasspath(cp);
return this;
}
public Script parse(String scriptText) {
return gs.parse(scriptText);
}
public static class FilteredCL extends GroovyClassLoader{
Pattern filterOut;
public FilteredCL(ClassLoader parent,Pattern excludeClassPattern){
super(parent);
filterOut = excludeClassPattern;
}
#Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
if(filterOut.matcher(name).matches())throw new ClassNotFoundException("class not found "+ name);
return super.loadClass(name, resolve);
}
}
}
now add a test case - actually you can move code from test case into a class...
import ... /* all katalon imports here*/
assert method1() == 'HELLO WORLD'
def method1() {
def gb = new GroovyBox(this.getClass().getClassLoader().getParent(), ~/^com\.google\..*/)
def script = gb.parse('''
#Grab(group='com.google.api-client', module='google-api-client', version='1.31.3')
import com.google.api.client.http.HttpRequest
def c = HttpRequest.class
println( "methods execute:: "+c.methods.findAll{it.name=='execute'} )
println( "methods setResponseReturnRawInputStream:: "+c.methods.findAll{it.name=='setResponseReturnRawInputStream'} )
println greeting
return greeting.toUpperCase()
''')
script.setBinding([greeting:'hello world'] as Binding)
return script.run()
}
options to define external dependencies:
#Grab(...) as a first line of parsed script - loads all required dependencies from maven central (by default). for example #Grab(group='com.google.api-client', module='google-api-client', version='1.31.3') corresponds to this artifact.
sometimes you need to specify specific maven repository then add #GrabResolver(name='central', root='https://repo1.maven.org/maven2/')
if you want to specify local file dependencies then in the code above:
def gb = new GroovyBox(...).withClassPath([
'/path/to/lib1.jar',
'/path/to/lib2.jar'
])
I'm going through this example but something about it is very confusing to me: https://www.testcookbook.com/book/groovy/jenkins/intro-testing-job-dsl.html
In this test, how/what is executing getJobFiles()? I don't see it being called anywhere. Is there some magic with jobFiles? Is specifying jobFiles somehow calling getJobFiles?
import javaposse.jobdsl.dsl.DslScriptLoader
import javaposse.jobdsl.plugin.JenkinsJobManagement
import org.junit.ClassRule
import org.jvnet.hudson.test.JenkinsRule
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
class JobScriptsSpec extends Specification {
#Shared
#ClassRule
JenkinsRule jenkinsRule = new JenkinsRule()
#Unroll
def 'test script #file.name'(File file) {
given:
def jobManagement = new JenkinsJobManagement(System.out, [:], new File('.'))
when:
new DslScriptLoader(jobManagement).runScript(file.text)
then:
noExceptionThrown()
where:
file << jobFiles
}
static List<File> getJobFiles() {
List<File> files = []
new File('jobs').eachFileRecurse {
if (it.name.endsWith('.groovy')) {
files << it
}
}
files
}
}
Edit
It seems like jobFiles does call getJobFiles() but I don't understand how. Is this a groovy or spock feature? I've been trying to research this but can finding anything explaining this in detail.
This is standard Groovy functionality. You can abbreviate any getter call like
def file = new File("sarek-test-parent/sarek-test-common/src/main/java")
println file.name // getName()
println file.parent // getParent()
println file.absolutePath // getAbsolutePath()
println file.directory // isDirectory()
java
sarek-test-parent\sarek-test-common\src\main
C:\Users\alexa\Documents\java-src\Sarek\sarek-test-parent\sarek-test-common\src\main\java
true
The same works for setters:
new Person().name = "John" // setName("John")
new Person().zipCode = "12345" // setZipCode("12345")
Actually the second link provided by jaco0646 explains it, just his mixing up this simple fact with data providers clouds the explanation.
Edit
When Groovy determines that jobFiles does not refer to any existing variable, it considers the name as a Groovy property, which then allows it to take advantage of the shortcut notation for accessing properties.
I have a groovy script that defines a method which throws an exception.
Using AST Transformations I generate at compile time a new class.
Then I copy this method from the script to this new class and make the class available at runtime.
When at runtime I create a new object of the new class and call the method in stack trace I can see references to Script1 class instead of the new generated class.
Exception in thread "main" java.lang.RuntimeException
at MyGeneratedClass.myMethod(Script1.groovy:4)
at MyGeneratedClass$myMethod.call(Unknown Source)
at scripttest.ExTest.main(ExTest.groovy:35)
Is there anything I can do to change it so I don't see this Script1.groovy class in stack trace but the new class and line number within it?
My code:
class ExTest {
public static void main(String[] a) {
String script = '''
def myMethod() {
throw new RuntimeException()
}
'''
def config = new CompilerConfiguration()
config.addCompilationCustomizers(new MyCompilerConfiguration())
ClassLoader classLoaderToUse = new GroovyClassLoader()
GroovyShell shell = new GroovyShell(classLoaderToUse, new Binding(), config)
Script parsedScript = shell.parse(script)
def generatedClass = parsedScript.class.fields.find {it.name == 'myGeneratedClassField'}.type
def generated = generatedClass.newInstance()
generated.myMethod()
}
}
class MyCompilerConfiguration extends CompilationCustomizer {
MyCompilerConfiguration() {
super(CompilePhase.CONVERSION)
}
#Override
void call(SourceUnit source, GeneratorContext context, ClassNode currentClassNode) throws CompilationFailedException {
def newClassAst = new AstBuilder().buildFromSpec {
classNode('MyGeneratedClass', ClassNode.ACC_PUBLIC) {
classNode java.lang.Object
interfaces { classNode GroovyObject }
mixins { }
}
}
ClassNode myGeneratedClassNode = newClassAst[0]
source.getAST().addClass(myGeneratedClassNode)
currentClassNode.addField('myGeneratedClassField', Opcodes.ACC_PUBLIC, myGeneratedClassNode, new EmptyExpression())
MethodNode myMethodNode = source.getAST().methods.find {it.name == 'myMethod'}
myGeneratedClassNode.addMethod(myMethodNode.name, Opcodes.ACC_PUBLIC, myMethodNode.returnType, myMethodNode.parameters, myMethodNode.exceptions, myMethodNode.code)
}
}
Your stacktrace is correct, the Script1.groovy:4 you see is not the name of a class, but the name of the file which has generated this class, which is, in your case, a Groovy File.
This name come from the CodeSource instance present in the CompilationUnit associated with the ClassNode. You can change it by using a GroovyCodeSource in methods like GroovyShell.parse or GroovyClassLoader.parseClass, but I think it's a really bad idea ! (security, debugging and other thinks are related to this object)
There is an application where users can provide custom groovy scripts. They can write their own functions in those scripts. I want to restrict people from using the 'synchronized' keyword as well as some other keywords in these scripts. For example it should not be possible to write a function like below.
public synchronized void test() {
}
I am creating a CompilerConfiguration and using the SecureASTCustomizer. However adding org.codehaus.groovy.syntax.Types.KEYWORD_SYNCHRONIZED to the list of black listed tokens doesn't seem to do the job. (if I add org.codehaus.groovy.syntax.Types.PLUS it's preventing the usage of '+' within scripts.. but doesn't seem to do the job for synchronized)
Any ideas on how to achieve this ...
You can do something like this:
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.syntax.SyntaxException
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.classgen.GeneratorContext
class SynchronizedRemover extends org.codehaus.groovy.control.customizers.CompilationCustomizer {
SynchronizedRemover() {
super(org.codehaus.groovy.control.CompilePhase.CONVERSION)
}
void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) {
classNode.methods.each { mn ->
if (mn.modifiers & 0x0020) { // 0x0020 is for synchronized
source.addError(new SyntaxException("Synchronized is not allowed", mn.lineNumber, mn.columnNumber))
}
}
}
}
def config = new CompilerConfiguration()
config.addCompilationCustomizers(new SynchronizedRemover())
def shell = new GroovyShell(config)
shell.evaluate '''
class Foo { public synchronized void foo() { println 'bar' } }
'''
The idea is to create a compilation customizer that checks generated classes and for each method, add an error if the synchronized modifier is present. For synchronized block inside methods, you can probably use the SecureASTCustomizer with a custom statement checker.