Not sure if this helps….
I’ve programmatically loaded ivy assets using groovy.grape.Grape./grab/
//
//
I’ve not had luck with developers remembering the grap grab with
systems classloader annotations, and I got tired of answering how to
or pointing to docs, so I do it for them in my database connection
code… //
Instead of my devs placing this at the top of their script…
@Grapes([
@GrabConfig(systemClassLoader=true),
@Grab(“net.snowflake:snowflake-jdbc:3.16.1”)
])
import groovy.sql.Sql
I bake this in my compiled groovy class for connecting...
Grape.*/instance/*.grabWithRootLoader(“net.snowflake:snowflake-jdbc:3.16.1”)
see My “Grape” class below
*package*script.util
*import*script.Env
@groovy.util.logging.Slf4j
@Singleton
*class*Grape {
Set grapes= [] // grapes that have been loaded
/**
* Grab a grape using the root class loader.
* This should be the same class loader as used with this GrabConfig
* <p/>
* <pre>
* {@code @}Grapes([
* {@code @}GrabConfig(systemClassLoader=true)
* {@code @}Grab(.....)
* ])
* </pre>
* <p/>
*
* *@param*grape format "group:module:version" or "property:key:default"
*/
*void*grabWithRootLoader (String grape) {
grab(Env./script/.getClass().getClassLoader().getRootLoader(),grape)
}
/**
* Grab a grape using the system class loader
* *@param*grape, format = "group:module:version" or
"property:key:default"
*/
*void*grabWithSystemLoader (String grape_string) {
grab(Env./script/.getClass().getClassLoader()./getSystemClassLoader/(),grape_string)
}
/**
* Grab a grape using the _Groovy_ class loader
* *@param*grape, format = "group:module:version" or
"property:key:default"
*/
*void*grabWithGroovyLoader (String grape_string) {
grab(*new*groovy.lang.GroovyClassLoader(),grape_string)
}
/**
* Grab a grape using a class loader
* *@param*classLoader
* *@param*grape_string, format = "group:module:version" or
"property:key:default_grape_string"
*/
*void*grab (ClassLoader classLoader, String grape_string) {
*if*(!grape_string) *return*
*if*(grapes.contains(grape_string)) *return*// already loaded
*if*(!classLoader) classLoader= *new*groovy.lang.GroovyClassLoader()
List grape= grape_string.split(//://)
*if*(grape[0] == 'property') {
String grape_property_key= grape[1]
grape_string= Env./props/.getProperty(grape_property_key)
*if*(!grape_string) {
grape_string= grape.drop(2).join(':') // default_grape_string
*/log/*.warn("grape not defined in property [{}], using default grape
[{}]",grape_property_key,grape_string)
}
grape= grape_string.split(//://)
}
*def*(group, module, version) = grape
*if*(grapes.contains(grape_string)) *return*// already loaded
*try*{
*/log/*.debug("grabbing [{}] using class loader
[{}]",grape_string,classLoader.getClass().getName())
groovy.grape.Grape./grab/(classLoader:classLoader,[[group:group,module:module,version:version]]
*as*Map[])
grapes<< grape_string
}
*catch*(e) {
*/log/*.error("grab of [{}] using class loader [{}] failed
[{}]",grape_string,classLoader.getClass().getName(),e.message)
}
}
}
*From: *Per Nyfelt <p...@alipsa.se>
*Date: *Friday, July 11, 2025 at 11:54 AM
*To: *users@groovy.apache.org <users@groovy.apache.org>
*Subject: *[EXT] Re: using @GrabConfig(systemClassLoader=true) in a
ScriptEngine
Thank you for the suggestions! I tried setting the system classloader
with
-Djava.system.class.loader=org.codehaus.groovy.tools.RootLoader
and also tried
-Djava.system.class.loader=groovy.lang.GroovyClassloader
That indeed fixes the issue with "no suitable classloader available
for Grab" but unfortunately neither of them fixes the problem.
The issue is now that ant (called via AntBuilder) is unable to find
the h2 jdbc driver. It looks like RootLoader is a child first
classloader so it "should" have worked but I guess that ant's own
classloader is not playing well that setup.
The code is here in case anyone want to try it out:
https://github.com/perNyfelt/groovy-issues/tree/main/antbuilder
<https://secure-web.cisco.com/1Bx2GK0pQvXmkM2N8lyoS5W5TPxsShSurQWW-ab_KLN-nOcWfbH6IzoprBHWbo5Q8HmIlTIMq5GmlsWJxfh__F3Sd1S3R0z61Ep1AHqKJ_wRt9sMJCbCS6b1QneiglANZ4bE-1y07xFAK5dMw0q2Ot021HUX5dBUx9Ci5YH_xCYDNEMgrmkeDEDbbP2uYIbI9fIWJ8yd_vl35ohCyW7pcvm5pzQOrnfMLO0A7_pHvfh8DSW6c8MJ4DmTvXT0VZ1HUqhqtmYTlvoSpIhU-tf2G8O2GSTjDlme--WlVFeZaPAm7dNZKAoCQOKJv1Qr96wykFBrB_tP7GnO6IkTNSTqS7N5-Nc6a6I8OFZP_5JwQQi6KcIWJar2nX2iV-ZvMLT-AqZOM-OAULRXD1Y18VMYEGmlRFfdqcCMil5rTdLZMbXc/https%3A%2F%2Fgithub.com%2FperNyfelt%2Fgroovy-issues%2Ftree%2Fmain%2Fantbuilder>
The error i am getting is as follows:
[0.003s][warning][cds] Archived non-system classes are disabled
because the java.system.class.loader property is specified (value =
"org.codehaus.groovy.tools.RootLoader"). To use archived non-system
classes, this property must not be set
Exception in thread "main" : Class Not Found: JDBC driver
org.h2.Driver could not be loaded
at
org.apache.tools.ant.taskdefs.JDBCTask.getDriver(JDBCTask.java:434)
at
org.apache.tools.ant.taskdefs.JDBCTask.getConnection(JDBCTask.java:365)
at
org.apache.tools.ant.taskdefs.SQLExec.getConnection(SQLExec.java:953)
at
org.apache.tools.ant.taskdefs.SQLExec.execute(SQLExec.java:649)
at
org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:299)
at
java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at
org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:99)
at groovy.ant.AntBuilder.performTask(AntBuilder.java:347)
at groovy.ant.AntBuilder.nodeCompleted(AntBuilder.java:286)
at
groovy.util.BuilderSupport.doInvokeMethod(BuilderSupport.java:161)
at groovy.ant.AntBuilder.doInvokeMethod(AntBuilder.java:219)
at
groovy.util.BuilderSupport.invokeMethod(BuilderSupport.java:75)
at
org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:651)
at
org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:628)
at
org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeOnDelegationObjects(ClosureMetaClass.java:392)
at
org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:331)
at
groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007)
at
org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
at sqltask$_run_closure1.doCall(sqltask.groovy:9)
at
java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at
org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
at
org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:280)
at
groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007)
at groovy.lang.Closure.call(Closure.java:433)
at groovy.lang.Closure.call(Closure.java:422)
at
org.codehaus.groovy.runtime.DefaultGroovyMethods.with(DefaultGroovyMethods.java:368)
at
org.codehaus.groovy.runtime.DefaultGroovyMethods.with(DefaultGroovyMethods.java:313)
at org.codehaus.groovy.runtime.dgm$914.doMethodInvoke(Unknown
Source)
at
org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
at sqltask.run(sqltask.groovy:7)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:460)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:504)
at SqlTask.main(SqlTask.java:10)
I guess my best option is to mimic what the groovy command does
exactly and try to get that to work from java...
regards,
Per
*Jochen Theodorou*- Wednesday, July 9, 2025 5:46:19 PM GMT+2
If you run the program using the groovy command or a shell then there is
a GroovyClassLoader involved can be used instead of the systemloader.
Basically since Java9 Java does not use a url class loader as system
loader anymore. Which means we cannot support systemClassLoader=true in
case there is no rescuing GroovyClassLoader
you could try using -Djava.system.class.loader=... to set a class loader
using the fully qualified name. It should be a URLClassloader or child
of it and I guess the class needs to be on the classpath as well
The alternative is using a small program to load the classpath and start
the program. In Groovy we are using RootLoader for this. Or you use
GroovyStarter
bye Jochen
On 7/8/25 12:51, Per Nyfelt wrote:
Hi,
I am trying to figure out a way to
use @GrabConfig(systemClassLoader=true) in a ScritpEngine (or
GroovyShell, does not matter.
The following script works without any problem using the groovy
command (.e.g `groovy sqltaskExample.groovy`):
@GrabConfig(systemClassLoader=true)
@Grab('com.h2database:h2:2.3.232')
import groovy.ant.AntBuilder
println "Groovy ClassLoader: $*{*this.class.classLoader*}*"
def project = new AntBuilder()
project.with *{*
**sql(
driver: "org.h2.Driver",
url: "jdbc:h2:mem:AZ",
userid: "sa",
password: "",
// direct printed output into a text file:
output: "query.out",
print: "yes", // enable printing of result sets
showheaders:"false", // suppress column names
showtrailers:"false" // suppress "N rows returned" line
) *{*
**transaction("""
CREATE TABLE some_table (
id INT,
name VARCHAR(200)
);
""")
transaction("""
INSERT INTO some_table (id, name)
VALUES (1, 'hello');
""")
transaction("""
SELECT name
FROM some_table
WHERE id = 1;
""")
*}*
**// now the file query.out contains exactly "hello"
loadfile(property: "row1col1", srcFile: "query.out")
echo(message: 'row1col1 = ${row1col1}')
delete(file: "query.out")
*}*
Calling that from java however does not work:, e.g.
public class ShellWithGrabSupport {
public static void main(String[] args) throws Exception {
GroovyScriptEngine gse = new GroovyScriptEngine(".");
gse.run("sqltaskExample.groovy", new Binding());
}
}
Results in "No Suitable classloader found for Grab"
I have tried stuff like this:
System./setProperty/("groovy.grape.enable", "true");
System./setProperty/("groovy.grape.report.downloads", "true");
GroovyClassLoader gcl = new
GroovyClassLoader(ClassLoader./getSystemClassLoader/());
CompilerConfiguration config = new CompilerConfiguration();
config.setRecompileGroovySource(true);
GroovyShell shell = new GroovyShell(gcl, config);
Script s = shell.parse(new File("sqltaskExample.groovy"));
s.run();
and
System./setProperty/("groovy.grape.report.downloads", "true");
System./setProperty/("groovy.grape.enable", "true");
// Ensure system classloader is used
Thread./currentThread/().setContextClassLoader(ClassLoader./getSystemClassLoader/());
// This mimics what the `groovy` CLI does
GroovyMain./main/(new String[]{"sqltaskExample.groovy"});
All with the same result.
I use the following to bootstrap java:
gl="$GROOVY_HOME/lib"
v="4.0.27"
cp="$gl/groovy-$v.jar:$gl/groovy-ant-$v.jar:$gl/groovy-ant-$v.jar:$gl/ant-1.10.15.jar:\
$gl/ant-launcher-1.10.15.jar:$gl/ivy-2.5.3.jar"
java -cp "$cp" ShellWithGrabSupport.java
So since ant is loaded by the system classloader, I must
use @GrabConfig(systemClassLoader=true) in the groovy script for
the jdbc driver, otherwise the sql ant task will not be able to
find it.
This minimum, reproducible example is here:
https://github.com/perNyfelt/groovy-issues/tree/main/grabconfig
<https://secure-web.cisco.com/1In-4b3_kzgWRocKvIlRp522Qr7T5nJ8OLk87IATlUz8-gcWUthaRCh0X0vCNZLB3VN8mZ1KQgTB6ncGl8MPCgIj8GiNm6l-CMGFX9aDFgkYJ6USEMT-O3oaIv6ho_9-BqmK9KNOEuk_pZ3dg6dKBnihk7bW_27BFRpD780XfN5m0SyDyIAEwlL7z_IJTYoh5wheti2xBYdNvUkEgevL-Yqotv7r_bT1LF6qnDFJX6iiN8CzQ285ngU65QyRhZQAqIfTUoGqq3yJ8f1IlK_0QFOOvgkdmi3VfgNrPF0oDWFR_kUQK-JeA2zQsIc2WhLS_569RgelvhftCSGGib5VCIdijIlgKmsiNKrMw83d3eSOhU7jHp4ba0aljnMU_VopgMuTapABIU-veEZKxFI5HY98GcHQgE3gQRF1UxYvln40/https%3A%2F%2Fgithub.com%2FperNyfelt%2Fgroovy-issues%2Ftree%2Fmain%2Fgrabconfig>
The details of the error are as follows:
Exception in thread "main"
org.codehaus.groovy.control.MultipleCompilationErrorsException:
startup failed:
General error during conversion: No suitable ClassLoader found
for grab
java.lang.RuntimeException: No suitable ClassLoader found for grab
at
groovy.grape.GrapeIvy.chooseClassLoader(GrapeIvy.groovy:179)
at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:273)
at groovy.grape.Grape$1.run(Grape.java:172)
at groovy.grape.Grape$1.run(Grape.java:158)
at
java.base/java.security.AccessController.doPrivileged(AccessController.java:319)
at groovy.grape.Grape.grab(Grape.java:158)
at
groovy.grape.GrabAnnotationTransformation.visit(GrabAnnotationTransformation.java:380)
at
org.codehaus.groovy.transform.ASTTransformationVisitor.lambda$addPhaseOperationsForGlobalTransforms$5(ASTTransformationVisitor.java:374)
at
org.codehaus.groovy.control.CompilationUnit$ISourceUnitOperation.doPhaseOperation(CompilationUnit.java:906)
at
org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:701)
at
org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:675)
at
groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:365)
at
groovy.lang.GroovyClassLoader.lambda$parseClass$2(GroovyClassLoader.java:314)
at
org.codehaus.groovy.runtime.memoize.StampedCommonCache.compute(StampedCommonCache.java:163)
at
org.codehaus.groovy.runtime.memoize.StampedCommonCache.getAndPut(StampedCommonCache.java:154)
at
groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:314)
at
groovy.util.GroovyScriptEngine$ScriptClassLoader.doParseClass(GroovyScriptEngine.java:231)
at
groovy.util.GroovyScriptEngine$ScriptClassLoader.parseClass(GroovyScriptEngine.java:218)
at
groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:298)
at
groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:263)
at
groovy.util.GroovyScriptEngine.loadScriptByName(GroovyScriptEngine.java:534)
at
groovy.util.GroovyScriptEngine.createScript(GroovyScriptEngine.java:584)
at
groovy.util.GroovyScriptEngine.run(GroovyScriptEngine.java:571)
at ShellWithGrabSupport.main(ShellWithGrabSupport.java:7)
at
...
Does anyone know a way?