6

I have a groovy script createWidget.groovy:

 import com.example.widget

 Widget w = new Widget()

This script runs great when I run it like this:

$ groovy -cp /path/to/widget.jar createWidget.groovy

But, I wanted to hardcode the classpath within the script, so that users do not need to know where it is, so I modified createWidget.groovy as follows (which is one of the ways to modify classpath in groovy):

this.getClass().classLoader.rootLoader.addURL(new File("/path/to/widget.jar").toURL())

import com.example.widget

Widget w = new Widget()

But this always fails with a runtime error on the import: unable to resolve class com.example.widget.

This does look unorthodox and I am thinking you can't mess with the rootLoader prior to an import or is it something else?

ataylor
  • 64,891
  • 24
  • 161
  • 189
rouble
  • 16,364
  • 16
  • 107
  • 102
  • 1
    It might be easier to just write a batch file or bash script to run groovy with the cp parameter? – tim_yates Apr 25 '13 at 16:15
  • And you could test your import theory by removing it and trying `def w = Class.forName( 'com.example.Widget' ).newInstance()` in place of the constructor... – tim_yates Apr 25 '13 at 16:23

4 Answers4

10
// Use the groovy script's classLoader to add the jar file at runtime.
this.class.classLoader.rootLoader.addURL(new URL("/path/to/widget.jar"));

// Note: if widget.jar file is located in your local machine, use following:
// def localFile = new File("/path/tolocal/widget.jar");
// this.class.classLoader.rootLoader.addURL(localFile.toURI().toURL());

// Then, use Class.forName to load the class.
def cls = Class.forName("com.example.widget").newInstance();
Bae Cheol Shin
  • 1,498
  • 1
  • 11
  • 10
  • 1
    unfortunately not working for me rootLoader does not contain addURL method (classLoader does) URL format incorrect - should be "file:///path/to/widget.jar" Class.forName returning exception java.lang.ClassNotFoundException – Victor Dronov Jan 22 '20 at 00:06
0

If I understand the question, then you're asking for a self-contained unit which can be delivered to users.

This is possible using the basics of 'jar' on the JVM.

For example, this project plays a simple game called War-O. Users can run it with this command:

java -jar warO.jar [args]

The technique includes: (a) compile Groovy to class files and (b) include all necessary jars in the jar (including groovy-all). Also, the jar must have the "main" entry point specified in the manifest (which would be a modified version of your script).

The War-O project uses Gradle (see build file here), but the principle applies even if using Ant, Maven, etc. Using Gradle as an example:

jar.archiveName 'warO.jar'
jar.manifest {
    attributes 'Main-Class' : 'net.codetojoy.waro.Main'
    attributes 'Class-Path' : 'jars/groovy-all-1.6.4.jar jars/guava-collections-r03.jar jars/guava-base-r03.jar'
}
Michael Easter
  • 23,733
  • 7
  • 76
  • 107
0

The other way can be to just write down the java code to load class from jar file and as per groovy modify that code.

import java.net.URL;
import java.net.URLClassLoader;
import java.lang.reflect.Method;
public class JarFileLoader 
{
    public static void main (def args)
    {
        try
        {
            URLClassLoader cl = new URLClassLoader (new URL("jar:file:///path/to/widget.jar!/"));

            System.out.println ("Attempting...");

            Class beanClass = cl.loadClass ("com.example.widget.WidgetClass");
            Object dog = beanClass.newInstance();

            Method method = beanClass.getDeclaredMethod ("setBean", String.class);
            method.invoke (dog, "Who let the dog out");

            method = beanClass.getDeclaredMethod("getBean", null);            
            Object retObj = method.invoke (dog, null);

            String retVal = (String)retObj;

            System.out.println(retVal);
            System.out.println("Success!");
        }
        catch (Exception ex)
        {
            System.out.println ("Failed.");
            ex.printStackTrace ();
        }
    }
}
snoop
  • 171
  • 2
  • 12
-1

Groovy is a compiled language, and class names must be resolvable at compile time. Hence adding the Jar at runtime is not sufficient here.

(The import statement is also wrong, you'd have to append .* or .Widget. But this won't solve the deeper problem.)

Peter Niederwieser
  • 121,412
  • 21
  • 324
  • 259
  • This is not true of Groovy. Groovy is dynamic and often doesn't even know the class you are referring to--it uses "Duck Typing" to just try a method at runtime and see if it works. Java needs at least the interface or base class available at compile time but does not need the final class at compile time (this is why you can pass your own object into a collection even though your object wasn't available when the collection was compiled and why Tomcat can deal with your war without being recompiled) – Bill K Jan 16 '19 at 00:45