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?


Reply via email to