Friday, January 18, 2008

DRY Groovy, How To Get Groovy To Import A Class Into a Script

UPDATE: Read to the bottom to see another way to tackle this problem

Each time you repeat yourself in code, you may as well leave a bear trap by your desk. In fact, the bear trap is probably safer for me. Whenever I see repeated code, I want to poke my eyes out. I do my best to keep my code dry whenever reasonable.

So, when I started writing Groovy scripts, it became obvious pretty quickly that I had some utility classes waiting to break out. The only problem was, I couldn't find the technique for importing a Groovy class from another file into the current script. Thanks go to Peter Niederwieser for the solution.

Here's the setup:

Two Groovy files:
c:\tools\groovy\myscript.groovy
c:\tools\groovy\MyClass.groovy

The content of MyClass.groovy:

class MyClass {
def howdy() {
println 'Howdy'
}
}

The content of myscript.groovy:
new MyClass().howdy()


Super simple. However, no matter how I tried to run myscript, I got this error message:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed,
c:\tools\groovy\myscript.groovy: 1: unable to resolve class MyClass

What gives? The files were right next to each other, but stupid Groovy couldn't find them. That's when Peter reminded me that groovy will look in the classpath to resolve classes.

There are two ways (that I know of) to tell Groovy what your classpath is. You can either indicate it on the command line as in:
groovy -cp c:\tools\groovy myscript.groovy

Or, you can set the CLASSPATH environment variable to include the path where the collaborating class (in this case, MyClass.groovy) lives.

Using the classpath, you don't even have to keep your script and its dependencies in the same directory. I simply set my CLASSPATH, and now I'm off to the races. Now, I keep all my scripts in c:\tools\groovy (which is on my PATH), and my classes in c:\tools\groovy\classes (which is on my CLASSPATH).

WARNING: For all of you Windows users out there, the name of the Groovy file IS CASE SENSITIVE! So, if you want Groovy to find class MyClass, it had better be in something like, %CLASSPATH%\MyClass.groovy. %CLASSPATH%\myclass.groovy did NOT work for me. This would make complete sense to me if I were in a Unix environment. However, I am used to my Windows machines being mostly case insensitive. This is a bit odd, but understandable I guess.

UPDATE: Jochen Theodorou has pointed me in the right direction for another solution that I like better. You don't have to modify the classpath to cause groovy to find the classes that you are importing. Instead, you can add load information to your %GROOVY_HOME%\conf\groovy-starter.conf file. Mine now has these extra lines:
# load classes for local scripts
load c:/tools/groovy/classes

Now, I don't have to keep the CLASSPATH environment variable set and worry about how that interacts with Java.

There you have it. Now, you can keep your scripts dry.

6 comments:

Jochen "blackdrag" Theodorou said...

I would have said this on the mailing list, but to keep the information at most public place... I would like to say 2 things:

1) I discourage from using the CLASSPATH variable not only for Groovy, but also for Java... Just bad experience

2) the groovy command line command looks in the current directory for scripts too. so if you had changed in the directory where your scripts are, or to the root of all your scripts (in case you are using packages), then it would have simply worked. The -cp option is only required if the script is in none of the default locations... which can also be changed in the groovy-starter.conf file.

Eric Anderson said...

Thanks for the feedback. I've updated the main body of the post.

Anonymous said...

The groovy command line is *supposed* to look in the current directory; didn't work for me. I had to use the groovy -cp ./ test.groovy option to get test script to see a class in another script sitting in the same directory.

Windows XP SP 2, latest groovy install.

Eric Anderson said...

I am seeing the same thing. I thought that at one time Groovy was finding classes in sibling (same directory) files. However, it does not for me either. XP SP2, latest Groovy install.

matt said...

Even Jochen's solution is a half-cocked supplement for the problem. If Groovy is to be a true scripting language, then it should include all paths in PATH as potential candidates for source code.

Just my twopence.

Eric Anderson said...

@Matt

Agreed that this feels like a weird place to set the path information. But, it is pretty similar to setting a Groovy scripts path environment variable.

I'm not sure I want all my Groovy classes to be on the PATH. I like having a separate setting.

This becomes a problem when dealing with scripts that are shared by a team. In the case of shared scripts, I am more inclined to build a full project with a compiled .jar.