I have written a tiny Groovy script (just a few simple instructions) and named it a-b.groovy.
My colleague has trouble running it and said:
I believe '-' is illegal in Groovy scripts filenames
Question: What is the complete list of characters that are illegal in Groovy filenames?
(Note: Unlike Naming convention for groovy script files, my question is not about conventions)
The most accurate answer is to use the same standards for naming Groovy scripts as we use for naming Java classes. Groovy, similarly to Java supports Unicode characters encoding and you can most probably use any Unicode character as a script name (it doesn't mean you should do it).
Every Groovy script gets compiled to a Java class that extends groovy.lang.Script class. Its name is taken from a Groovy script file name. Take a look at this example:
someSimpleGroovyScript.groovy
println "test"
When we compile it with groovyc someSimpleGroovyScript.groovy we will see a class file someSimpleGroovyScript.class. Now if we take a look what does this class look like:
javap someSimpleGroovyScript
we will get something like this:
Compiled from "someSimpleGroovyScript.groovy"
public class someSimpleGroovyScript extends groovy.lang.Script {
public static transient boolean __$stMC;
public someSimpleGroovyScript();
public someSimpleGroovyScript(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
protected groovy.lang.MetaClass $getStaticMetaClass();
}
Corner cases for Groovy script names
Even though Java class naming standards apply to Groovy script names, there are some corner cases that Groovy supports.
Using - character in the script name
You can name your script as a-b.groovy and run as a Groovy script:
groovy a-b.groovy
You can even compile it with groovyc to get following Java class:
Compiled from "a-b.groovy"
public class a-b extends groovy.lang.Script {
public static transient boolean __$stMC;
public a-b();
public a-b(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
protected groovy.lang.MetaClass $getStaticMetaClass();
}
Even though this class name incorrect for Java compiler, you can still run it with Java (you need to add groovy-all to the classpath):
java -classpath ".:./groovy-all-2.4.12.jar" a-b
Using $ in the script name
In Groovy you can even call your script as 123$.groovy and you can simply run it as:
groovy 123\$.groovy
Groovy compiler however will replace $ with underscore when compiling this script to Java class:
javap 123_
Output:
Compiled from "123$.groovy"
public class 123_ extends groovy.lang.Script {
public static transient boolean __$stMC;
public 123_();
public 123_(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
protected groovy.lang.MetaClass $getStaticMetaClass();
}
You can still run such compiled script with Java:
java -classpath ".:./groovy-all-2.4.12.jar" 123_
Using space in the script name
You can also use a space in your script name, e.g. this is script.groovy can be executed as:
groovy this\ is\ script.groovy
Groovy compiler however will replace every space with a underscore:
Compiled from "this is script.groovy"
public class this_is_script extends groovy.lang.Script {
public static transient boolean __$stMC;
public this_is_script();
public this_is_script(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
protected groovy.lang.MetaClass $getStaticMetaClass();
}
You can still run such compiled script with Java:
java -classpath ".:./groovy-all-2.4.12.jar" this_is_script
Using class.groovy as a script name
It goes even further. You can create a script called class.groovy that will compile to a following Java class:
Compiled from "class.groovy"
public class class extends groovy.lang.Script {
public static transient boolean __$stMC;
public class();
public class(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
protected groovy.lang.MetaClass $getStaticMetaClass();
}
javac compiler would never allow you compiling a class with such name, however java has no problems with running such bytecode:
java -classpath ".:./groovy-all-2.4.12.jar" class
As the file name of the script is used to generate the corresponding class name, it would be restricted to valid java class names
Valid characters in a Java class name
You can have almost any character, including most Unicode characters!
The exact definition is in the Java Language Specification under
section 3.8: Identifiers.
An identifier is an unlimited-length sequence of Java letters and Java
digits, the first of which must be a Java letter. ...
Letters and digits may be drawn from the entire Unicode character set,
... This allows programmers to use identifiers in their programs that
are written in their native languages.
An identifier cannot have the same spelling (Unicode character
sequence) as a keyword (§3.9), boolean literal (§3.10.3), or the null
literal (§3.10.7), or a compile-time error occurs.
I encountered a weird issue with a script containing a dash in the filename and with a function having a Closure as parameter
Let's consider this exemple, test-script.groovy :
#!/usr/bin/env groovy
void foo(String msg, Closure clo)
{
println msg
clo()
}
foo 'bar', { -> println 'closure-bar'}
On my dev env with Groovy 2.4.16 it works like a charm, it prints :
bar
closure-bar
On the prod env with Groovy 2.4.5, I get this error :
Caught: java.lang.ClassFormatError: Illegal class name "test-script$foo" in class file test-script$foo
java.lang.ClassFormatError: Illegal class name "test-script$foo" in class file test-script$foo
at test-script.run(test-script.groovy:9)
If I rename test-script.groovy to testScript.groovy, it works for both version 2.4.16 and 2.4.5
I tracked down the "bug fix" to Groovy 2.4.15. Prior to this version, it failed.
The changelog for this version does not indicate any relevant fix so maybe it has been enclosed in another bug fix.
Anyway, I've been stuck a few hours on this case so if it can help someone, good thing !
Related
I want to use Micronaut from a Groovy script. It seems that annotations such as #Inject and #PostConstruct are not processed.
Here is the code I tried:
#!/usr/bin/env nix-shell
#!nix-shell -i groovy
#Grapes([
#Grab('ch.qos.logback:logback-classic'),
#Grab('io.micronaut:micronaut-runtime')
])
package org.sdf // NPE without package
import io.micronaut.runtime.Micronaut
import javax.inject.*
import javax.annotation.*
#Singleton
class Component {
}
#Singleton
class App implements Runnable {
#Inject
Component comp
#Override
#PostConstruct
public void run() {
// Never runs
assert this.comp != null
assert false
}
}
static void main(String... args) {
Micronaut.run(App, args);
}
It doesn't run post-construct method and logs this:
22:17:43.669 [main] DEBUG i.m.context.DefaultBeanContext - Resolved bean candidates [] for type: interface io.micronaut.runtime.EmbeddedApplication
22:17:43.671 [main] INFO io.micronaut.runtime.Micronaut - No embedded container found. Running as CLI application
How can I use Micronaut with dependency injection in a single-file Groovy script?
How can I use Micronaut with dependency injection in a single-file
Groovy script?
You would have to compile all of the code in your script with Micronaut's annotation processors configured on the compile time classpath for your script. Technically that could be done with a single script but as a practical matter I don't think many folks are going to have a good reason to do or build support for making that more easily done.
FYI: Not an answer to your question as asked but one simple alternative approach is to not define all of your bean related classes in a single script, instead define them in their own separate source files in a project configured to build with Maven or Gradle using our annotation processors and then consume those classes from your script using #Grab like you have for other dependencies.
To my surprise I have learned today that the following works just fine in Groovy:
import java.util.concurrent.*
def atomicBool = new atomic.AtomicBoolean(true)
i.e. after the star import, I can use a 'partial' package to refer to java.util.concurrent.atomic.AtomicBoolean.
Obviously, the same is not valid in Java:
import java.util.concurrent.*;
public class Sample {
public static void main(String[] args) {
// compiler error: 'atomic' cannot be resolved to a type
new atomic.AtomicBoolean(true);
}
}
So it seems that Groovy's idea of a package is similar to C++ (or C#) namespaces in this regard.
Question to the Groovy experts:
Is this by design or is it a (potentially unintended) side effect of the way the interpreter treats star imports?
If it is by design, can you point me to a section in the documentation or language specification where this behavior is documented? (There is no mention of this in the documentation on Star Import and neither in the language spec as far as I can tell or at least I couldn't find anything.)
Based on Groovy source code, this behavior seems to be intended. Before we dig deeper into Groovy internals you have to be aware of one thing - Groovy compiles to a bytecode that can be represented by a valid Java code. It means that Groovy code like the one form your example actually compiles to something like this (without compile static and type checked transformations):
import groovy.lang.Binding;
import groovy.lang.Script;
import java.util.concurrent.atomic.AtomicBoolean;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class test extends Script {
public test() {
CallSite[] var1 = $getCallSiteArray();
}
public test(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, test.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
AtomicBoolean atomicBool = (AtomicBoolean)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(AtomicBoolean.class, true), AtomicBoolean.class);
return var1[2].callCurrent(this, atomicBool);
}
}
As you can see this Java class uses full java.util.concurrent.atomic.AtomicBoolean import and this is actually what Groovy transforms your input source code to.
How does it happen?
As you may know Groovy builds Abstract Syntax Tree (AST) from the input source file and it iterates over all nodes (like expressions, variable definitions, method calls etc.) and applies transformations. Groovy uses class called ResolverVisitor that is designed to resolve types. When Groovy compiles your code it finds ConstructorCallExpression:
new atomic.AtomicBoolean(true)
It sees that the expected type of the object you are trying to create is atomic.AtomicBoolean, so ResolverVisitor starts resolving the type by calling resolveOrFail(type, cce); at line 1131.
It tries several resolving strategies that fail until it reaches method resolveFromModule at line 695. What happens here is it iterates over all star imports (single java.util.concurrent.* in your case), then it concatenates star import with the type name and it checks if qualified name created from this concatenation is a valid type class. Luckily it is in your case:
When type gets resolved, Groovy replaces initial type with this resolved valid type name in the abstract syntax tree. After this operation your input code looks more like this:
import java.util.concurrent.*
java.util.concurrent.atomic.AtomicBoolean atomicBool = new java.util.concurrent.atomic.AtomicBoolean(true)
This is what compiler gets eventually. Of course fully qualified name gets replaced with the import (this is what Java compiler does with qualified names).
Was this "feature" introduced by designed?
I can't tell you that. However we can read from the source code, that this happens on purpose and type resolving like this is implemented with intention.
Why it is not documented?
I guess no one actually recommend using imports that way. Groovy is very powerful and you are able to do many things in a many different ways, but it doesn't mean you should do so. Star imports are pretty controversial, because using star imports over explicit imports makes your code more error-prone due to possible class import conflicts. But if you want to know exact answer to this kind of questions you would have to ask Groovy language designers and core developers - they may give you straight answer without any doubts.
I created a simple interface and groovy script that implements this interface.
class groovyClass implements MySimpleInterface {
#Override
String description (){
return "Описание";
}
}
But when I call this function from groovy class loader I get just a couple of unknown symbols (like this :Широкая СЌР) instead of my cyrillic one.
I'm using Intellij Idea
Also, when I'm writing it directly new Label ("Описание") it works just fine, but when I'm requesting it from groovy class loader it doesn't work
I'm working on some integration tests in Groovy. There are a few hundred tests and I've been tasked with removing hardcoded ID's in favor of a single file with constants. For about 500 of the files, everything works as expected (Thanks, grep and sed): hardcoded ID's have been replaced with a constant:
old
package com.example.package.tests;
class SomeTest extends BaseGroovyTest {
#Test
public void testSomething() {
def merchantId = "1234"
...
}
}
new
package com.example.package.tests;
import com.example.package.util.ConstantMerchants
class SomeTest extends BaseGroovyTest {
#Test
public void testSomething() {
def merchantId = ConstantMerchants.MERCHANT_A
...
}
}
ConstantMerchants
package com.example.package.util;
public final class ConstantMerchants {
public static final String MERCHANT_A = "1234";
public static final String MERCHANT_B = "1111";
public static final String MERCHANT_C = "2222";
public static final String MERCHANT_D = "3333";
...
private ConstantMerchants() { }
}
However, while some files were changed successfully (so the hardcoded ID is replaced with a reference to ConstantMerchants and ConstantMerchants is imported), their tests can't run due to the error:
groovy.lang.MissingPropertyException: No such property: ConstantMerchants for class: com.example.package.tests.SomeTest
I've checked multiple times, and the only modifications to the files are the addition of an import station for ConstantMerchants, and the replacement of a string ID with a ConstantMerchants counterpart.
Removing the import statement and then having eclipse add it by pressing Ctrl+Shift+O (Organize imports) results in the same file I had before removing the import statement, and the error still occurring.
Any help in resolving this issue would be very much appreciated!
You mentioned that you have many tests running and some of them fails. That makes me think that there is a dependency problem of your tests, means maybe some tests are not allowed to have the class ConstantMerchants in their import correctly.
Do the following:
Try to change the class ConstantMerchants position where all the tests have an access to that class.
Another thing you should check is your IDE synchronization:
Delete the build directory.
Rebuild the project.
Check if that helped.
No, then check for any other synchronization issues of the IDE.
If it didn't work, try another thing:
Check your building tool if it has some limitations or any Environment variable it needs for running in the best way.
And do the follows:
Delete the build directory
Perform a synchronization to the project libraries.
Rerun the project.
Most of the time it works great and can help eliminate that error.
I hope it helps.
Apparently some class that was used by a class that was used by a class (...) that was used by my test referenced ConstantMerchants but didn't properly import it.
For some reason the compiler thought this was the fault of the class at the end of the usage chain so it kept saying my tests were missing references while it was actually some util class a few layers deep. Adding the import to that file fixed the issue.
I finally found this by grepping for files that contain ConstantMerchants but not the import statement. If anyone is interested, my grep command was:
grep -L -e "import .*ConstantMerchants" $(grep -E -f /mnt/c/Data/search_replaced.txt * -rclH)
Where search_replaced.txt contains a few patterns that match the replaced IDs.
I mainly program in groovy, but occasionally it's too slow. So I write a separate .java class, and put the code that needs to run faster in a java method and call that java method from my groovy code.
This causes me to end up with two separate files and two separate classes. Is there any way I could embed a java method right into the groovy file, maybe marking it with an annotation to indicate that it is java?
This is the idea behind groovy++. Marking a class or method with the #Typed annotation will cause it to use static typing instead of dynamic typing, while still retaining a lot of goodness of groovy.
While not exactly java, typed groovy++ methods generally perform about the same as java would.
More information on groovy++ is available at: https://code.google.com/p/groovypptest/wiki/Welcome
You don't need to do anything special.
Just write the Java class behind the groovy class. 99% of all Java source is valid groovy source as well.
class GroovyClass {
def a;
def doSomething(x,y) { return x*y; }
}
class JavaClass {
SomeType someVar;
public JavaClass() { /* ... */ } // some contructor
public void doit(String a, int b) {} // full typed method, that is java
}
Groovy++ is somethign completely different.
The JavaClass needs to have everything fully typed to be "Java" however your problem can be solved far easyer if you just use types in the relevant groovy methods.
class AnotherGroovyClass {
// typed
public Result someMethod(SomeArg arg1, SomeOtherArg arg2) {
}
def someVariable; // untyped
}
If you think the lack of speed comes from the dynamic nature of groovy then just use full types at the relevant points.