Click here to Skip to main content
12,625,447 members (36,961 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

6.7K views
53 downloads
5 bookmarked
Posted

Create Groovy Classes Dynamically

, 29 Apr 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Create Groovy classes at run-time and use them from Groovy or Java

Introduction

Using GroovyClassLoader.parseClass(), it is possible to create a new Groovy class dynamically at run-time and use it from a Groovy script or a Java application. GroovyClassLoader.parseClass() will parse a string passed to it and attempt to create a Groovy class. This way, it is possible to add the necessary imports, set the class name and add the fields to the class being created. Groovy automatically adds field getters and setters - if a field name is field1, getField1() and setField1() are generated. Adding methods is also possible this way, but it can also be done later by assigning closures to the created class MetaClass. This is convenient because parsing is avoided. Closure parameters become method parameters.

Creating the Class

The ClassBuilder.groovy file is as follows:

 package javainterop2

class ClassBuilder {

    GroovyClassLoader loader
    String name
    Class cls
    def imports
    def fields
    def methods

    def ClassBuilder(GroovyClassLoader loader) {
        this.loader = loader
        imports = []
        fields = [:]
        methods = [:]
    }

    def setName(String name) {
        this.name = name
    }

    def addImport(Class importClass) {
        imports << "${importClass.getPackage().getName()}" +
                ".${importClass.getSimpleName()}"
    }

    def addField(String name, Class type) {
        fields[name] = type.simpleName
    }

    def addMethod(String name, Closure closure) {
        methods[name] = closure
    }

    def getCreatedClass() {

        def templateText = '''
<%imports.each {%>import $it\n <% } %> 
class $name
{
<%fields.each {%>    $it.value $it.key \n<% } %>
}
'''
        def data = [name: name, imports: imports, fields: fields]

        def engine = new groovy.text.SimpleTemplateEngine()
        def template = engine.createTemplate(templateText)
        def result = template.make(data)
        cls = loader.parseClass(result.toString())
        methods.each {
            cls.metaClass."$it.key" = it.value
        }
        return cls
    }
}

Using the Class from Groovy

The test.groovy file in the same package:

 package javainterop2

import java.util.Calendar
def builder = new ClassBuilder(this.class.classLoader)
builder.setName("MyClass");

builder.addImport(Calendar)

builder.addField('field1', Integer)
builder.addField('field2', Integer)

builder.addMethod('sum') { field1 + field2 }

builder.addMethod('product') { field1 * field2 }

builder.addMethod('testCalendar') {
    println Calendar.getInstance().getTime()
}

Class myClass = builder.getCreatedClass()
def myInstance = myClass.newInstance()

myInstance.field1 = 1
myInstance.field2 = 2

println myInstance.sum()
println myInstance.product()

myInstance.setField2(1500)
println myInstance.getField2()

myInstance.testCalendar()

The class created by GroovyClassLoader is stored in a variable and can be instantiated using myClass.newInstance(). field2 can be accessed directly or using its setter setField2(). The java.util.Calendar class is imported and used in the class' testCalendar() method.

Using the Class from Java

Using a dynamically generated class from Java is not so straightforward. The class can be passed from Groovy to Java as a variable. test1.groovy creates another Groovy class:

 package javainterop2

static Class getDynamicClass(ClassLoader loader) {
    def builder = new ClassBuilder(new GroovyClassLoader(loader))
    builder.setName('AnotherClass')
    builder.addField('field1', Integer)
    builder.addField('field2', Integer)
    builder.addMethod('sum') { return field1 + field2 }

    return builder.getCreatedClass()
}

and in Java:

 package javainterop2;

import groovy.lang.GroovyObject;
import groovy.lang.GroovyShell;

import java.io.File;
import java.io.IOException;

import org.codehaus.groovy.control.CompilationFailedException;

public class Interop {

    public static void main(String[] args) {

        try {
            File file = new File("src/javainterop2/test1.groovy");
            GroovyShell shell = new GroovyShell();
            Class<?> AnotherClass = (Class<?>) shell.parse(file).invokeMethod(
                    "getDynamicClass", GroovyShell.class.getClassLoader());
            System.out.println(AnotherClass);
            try {
                GroovyObject o = (GroovyObject) AnotherClass.newInstance();
                o.setProperty("field1", 1);
                o.setProperty("field2", 2);
                Object[] arguments = {};
                System.out.println(o.invokeMethod("sum", arguments));
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (CompilationFailedException | IOException e) {
            e.printStackTrace();
        }
    }
}

test1.groovy is parsed by GroovyShell and getDynamicClass() is invoked to get the class variable. The new class instance is cast to GroovyObject and GroovyObject.setProperty(), GroovyObject.getProperty() and GroovyObject.invokeMethod() are used to manipulate the instance.

The Example Project

I added the Eclipse project I used for testing that contains all the files listed above. The project references groovy-all-2.2.2.jar which is located in the project's lib directory. The file is a part of the standard Groovy distribution (in the groovy/2.2.2/embeddable directory on my machine) and is used for invoking Groovy scripts from Java. Eclipse is Kepler Service Release 2.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Uros Calakovic
Systems / Hardware Administrator
Bosnia and Herzegovina Bosnia and Herzegovina
No Biography provided

You may also be interested in...

Pro

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161128.1 | Last Updated 29 Apr 2014
Article Copyright 2014 by Uros Calakovic
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid