groovy static class - groovy

What does the 'static' do in this file, sample.groovy?
static class froob {
}
The groovy code compiles fine with 'static' or without it.

There is absolutely no difference. The static in this situation is ignored.
To test, I created a groovy class and piped the output of "javap -verbose StaticTest" to a file. Then put "static" before the class def and piped that to a 2nd file. I then diffed the two files. The only differences were those unique ids that are associated with the long class ID that gets generated new for every class.

Are you referring to the static class or the static method "main" within the static class?
The keyword "static" means that allocation starts when the program begins and ends when the program ends. In other words, there's no way to programmatically create an instance of class "froob," but rather an instance is automatically create when the program beings and will exist until your program ends.
In the context of your small sample program, it is effectively meaningless. Only the "main" class needs to be static, as it needs to "exist" prior to any code within the program having been executed.

In Java, only an inner class can be static. Prior to Groovy 1.7 inner classes are prohibited, and declaring a top-level class static has no effect.
In Groovy 1.7+ I expect static inner classes to have the same semantics as they do in Java. Declaring a top-level class will likely have no effect (or be prohibited by the compiler).

Related

How to use GroovyClassLoader to load GroovyCompiled classes or how to set delegate in case of loadClass

I am trying to loadClass which are compiled during build and placed in classpath as .class files. For this I tried
GroovyClassLoader gos = new GroovyClassLoader();
gos.loadClass("className");
This will successfully load the class file in java code but it uses AppClassLoader to load this not GroovyClassLoader.
I understand that GroovyClassLoader internally finds it using AppClassLoader.
But there is a difference :
As gos.parseClass(string) will give us a class parsed from GroovyClassLoader.
While instantiating class file in second case(parseClass) give us delegate to set Delegate but in the first case(loadClass), we don't have any.
How to set delegate after doing loadClass or any way to load class file through GroovyClassLoader.
Load Class
Groovy Compiler compiles the groovy files(HelloWorld.dsl) and put them in class path creating .class file(HelloWorld.class) with same name.
By default compiled class file(open the byte code) have class(HelloWorld) with same name as of class file.
This class will get extended from Script. Now if you try to load this class file using
gos.loadClass("HelloWorld").newInstance() , it will give us a Script object and we won't be able to set a delegate to a Script object. Also it won't get converted into DelegatingScript.
To make your compile class extended from DelegatingScript add this at top of your dsl file #groovy.transform.BaseScript DelegatingScript delegatingScript . Groovy compiler will understand that and make the class files to get extended from DelegatingScript instead of Script as it was earlier.
Now when you try to load class using gos.loadClass("HelloWorld").newInstance(), it will give a DelegatingScript object not a Script object. To this object, we can set the delegate and run the script.
To use a delegate if you are using parseClass, we need to setScriptBaseClass as DelegatingScript to compilerConfiguration. Now if you try gos.parseClass(dslText).newInstance() , it will give directly give a DelegatingScript object and we can set a delegate and run the script.

How to declare a constructor or extends a class of a groovy script?

I am working on a shared library for Jenkins, and I want to access some utilities methods between some classes, but not all of them, thus I have established some statements:
I would like to avoid using static methods, since it does not access pipeline steps directly, and passing the pipeline instance every call would be a pain;
I would like to avoid a singleton as well, or prefixing every method call with the util class' instance;
Since it is not supposed to be shared between all classes I would like to avoid putting every method as a file on vars/ special directory, but I would like a similar behavior;
Despite extending the class would be a anti-pattern, it would be acceptable, though I would like to avoid the verbose Java syntax for declaring the class the same name as the file, once it is implicit in groovy;
This question does solve my problem partially, although there are issues with serialization, I noted that when I use checkpoint and some build is resumed from some stage, the instance loses all extra methods.
This other question would have helped me fix the serialization issue, however the author seems the have solved the root cause of his problem using a way that is not the original question titled for.
Is there a way to extends a implicit script class in groovy without using the class NameOfFile extends SomeOtherClass { put every thing inside this block } syntax? And without working with inner-class?
Or else, is there a way to declare a constructor using the script groovy syntax analogue as the previous question?
Or even, is there a way to change the serialization behavior to install the extra methods again after unserializing?
Appendix
The script syntax works more-or-less like this:
Consider the content of file src/cicd/pipeline/SomePipeline.groovy:
package cicd.pipeline
// there is no need to wrap everything inside class SomePipeline,
// since it is implicit
def method() {
// instance method, here I can access pipeline steps freely
}
def static otherMethod() {
// static method, here it is unable to access pipeline steps
// without a instance
}
#groovy.transform.Field
def field
def call() {
// if the class is used as method it will run
this.method()
SomePipeline.otherMethod() // or simply otherMethod() should work
this.field = 'foo'
println "this instance ${this.getClass().canonicalName} should be cicd.pipeline.SomePipeline"
}
// any code other than methods or variables with #Field
// annotation will be inside a implicit run method that is
// triggered likewise main method but isn't a static one
def localVar = 'foo'
println "It will not execute on constructor since it is on run: $localVar"
println "Method: ${org.codehaus.groovy.runtime.StackTraceUtils.sanitize(new Throwable()).stackTrace[0].methodName}"
println "this instance ${this.getClass().canonicalName} should be cicd.pipeline.SomePipeline"
If I was going to use the Java verbose syntax I would have to wrap almost everything inside a class SomePipeline which is implicit in groovy, this is the script syntax I want to keep.
I realised that this.getClass().superclass.canonicalName when outside Jenkins pipeline is groovy.lang.Script and when inside pipeline is org.jenkinsci.plugins.workflow.cps.CpsScript and based on this resource I was able to elaborate the following solution:
abstract class CustomScript extends org.jenkinsci.plugins.workflow.cps.CpsScript {
public CustomScript() {
// do something here, it will always execute regardless
// serialization, and before everything
}
}
#groovy.transform.BaseScript CustomScript baseScript
That is it, worked as expected! Of course you can elaborate this solution better in order to reduce repeating and avoid inner-classes, but I will leave it for your imagination.

Output resources using Groovy ASTTransformer

I've written a number of Java annotation processors that write some arbitrary data to text files that will be included in my class directory / jar file. I typically use code that looks like this:
final OutputStream out = processingEnv
.getFiler()
.createResource(StandardLocation.CLASS_OUTPUT, "", "myFile")
.openOutputStream();
I'm trying to do something similar in a groovy ASTTransformation. I've tried adding a new source file but that (expectedly) must be valid groovy. How do I write arbitrary resources from an ASTTransformation? Is it even possible?
As part of implementing your ASTTransformation, you need to implement the void visit(ASTNode[] nodes, SourceUnit source) method. In it you can call source.getConfiguration().getTargetDirectory() and it will return your build output directory, e.g. /Users/skissane/my-groovy-project/build/classes/groovy/main). You can then write your resources into there, and whatever is packaging them into the JAR (such as Gradle) should pull them from that.
In my case, I wanted to delay writing the resources until OUTPUT phase – since I was creating META-INF/services files, and I wanted to wait until I'd seen all the annotated classes before writing them, or else I'd be repeatedly adding to them for each annotated class – so I also implemented CompilationUnitAware, and then in my setCompilationUnit method I call unit.addPhaseOperation() and pass it a method reference to run during OUTPUT. Note, if you are using a local ASTTransformation, setCompilationUnit will be called multiple times (each time on a new instance of your transformation class); to avoid adding the phase operation repeatedly, I used a map in a static field to track if I'd seen this CompilationUnit before or not. My addPhaseOperation method is called once per an output class, so I used a boolean field to make sure I only wrote the resource files out once.
Doing this caused a warning to be printed:
> Task :compileGroovy
warning: Implicitly compiled files were not subject to annotation processing.
Use -implicit to specify a policy for implicit compilation.
1 warning
Adding this to build.gradle made the warning go away:
compileGroovy {
options.compilerArgs += ['-implicit:none']
}

Error while executing groovy script

Code is a follows:
class Book
{
private String title
Book (String theTitle)
{
title=theTitle
}
String getTitle()
{
return title
}
}
Book gina=new Book('Groovy in Action')
assert gina.getTitle()=='Groovy in Action'
assert getTitleBackwards(gina)=='noitcA ni yvoorG'
String getTitleBackwards(Book)
{
title=book.getTitle()
return title.reverse()
}
When I execute is with Ctrl+R, I get the following compilation error.
1 compilation error:
Invalid duplicate class definition of class Book : The source
Book.groovy contains at least two definitions of the class Book. One
of the classes is an explicit generated class using the class
statement, the other is a class generated from the script body based
on the file name. Solutions are to change the file name or to change
the class name. at line: 1, column: 1
Can anybody please explain me what is happening here.
Invalid duplicate class definition of class Book:
The code listing of the OP contains two parts:
The type definition of class Book
A groovy script that acts as a client of the Book type
Groovy treats your *.groovy file as either a script file or as a class definition file. A script file is a file that contains code that is not inside a class definition. When Groovy compiles a script file it implicitly creates a class to hold your code and the implicit class is given the name of the Book.groovy file.
Then, the compiler will try and create an additional class Book for the Book class defined in the groovy script, and the error occurs here because now there a actually two Book class definitions.
Compare: Blog entry with code sample for this error message
A way to define the Book class and the client script in the same file would be to rename the file, e.g. to BookApp.groovy. Caveat: if you do this, the Book type can only be referenced from within the script file, the Book type would not be found by groovy automatically, even if the *.groovy file was found on the class path.
Groovy console buffers items (sources, classes, variables) internally, second click of "run" can be different that first, I agree. Almost all interpreter windows (in any languages) has similar behaviour
has delicate differences when opening from File, pasting into window without file, in consequence can have name Book or ConsoleScript1 etc ("procedural" use of Groovy has hidden "object" background, hidden/default/generated class names from file etc)
This can be help-full by ad-hoc programming (script mode) but not always is the best for true OOP.
PS. code has few errors, too
when I ran into this error, it was because I forgot the word 'import' directly above so rather than :
import io.beapi.api.service.PrincipleService
#RestController
class UserController {
I had this:
io.beapi.api.service.PrincipleService
#RestController
class UserController {
The lack of the word import caused this issue for me. Quick easy fix (once I caught what it was) :)

Add constraints to properties of Groovy class (not Grails domain class!)

How can we add some common constraints (i.e. maxLength, nullable) to a property of a Groovy class? I know we can do it at Grails domain class, but is it possible if that is a Groovy class (I use it as a DTO class for my Grails project)?
Thank you so much!
You can add constraints to command classes. If a command class is in the same .groovy file as a controller (in Groovy you can have more than one public class in each .groovy file), you don't need to do anything special for Grails to recongise it as a command class.
However, if your command class is somewhere else (e.g. under src/groovy), you need to annotate it with #Validateable and add the package name to the grails.validateable.packages parameter in Config.groovy. Here's an example of a command that's not in the same file as a controller
pacakge com.example.command
#Validateable
class Person {
Integer age
String name
static constraints = {
name(blank: false)
age(size 0..100)
}
}
Add the following to Config.groovy
grails.validateable.packages = ['com.example.command']
Command classes have a validate() method added by Grails. After this method is called, any errors will be available in the errors property (as per domain classes).
Using a grails Command Object is probably your best bet. It has constraints and validation, but no database backing. It's normally a value object that controllers use, but you could instantiate one outside of a controller without any problems.
Not sure if this is relevant to your use (I am not familiar with DTOs), but in the current version (2.3.8), you can also add Grails constraints to an abstract class, and they will be inherited by the domains that extend it. Your IDE might not like it though ;)

Resources