javagrailsgroovyclasspathgant

NoClassDefFoundError GANT script on Grails


I'm trying to build some GANT script prior to my Grails Plugin. Basicly, i want to extend DefaultGrailsTemplateGenerator Class with new Class which add new method to auto generate service class by Grails Templating scheme.

Suppose i've named my class with Service generator which extends DefaulGrailsTemplateGenerator and add generateService method.

import org.codehaus.groovy.grails.scaffolding.DefaultGrailsTemplateGenerator;

class ServiceGenerator extends DefaultGrailsTemplateGenerator {
        void generateService(GrailsDomainClass domainClass, String destdir) {
        }
}

All things went normally (e.g GANT Script was called), except in a line of code which i instanstiated ServiceGeneratorClass, it returns error :

Error Error executing script GenerateService: com.cygnus.grails.util.scaffolding.ServiceGenerator
java.lang.NoClassDefFoundError: com.cygnus.grails.util.scaffolding.ServiceGenerator
    at GenerateExt_groovy.class$(GenerateExt_groovy)
    at GenerateExt_groovy.$get$$class$com$cygnus$grails$util$scaffolding$ServiceGenerator(GenerateExt_groovy)
    at GenerateExt_groovy.generateForDomainClass(GenerateExt_groovy:81)

I've tried to browse on this error and try to call the class by change the syntax from :

def templateGenerator = new ServiceGenerator(classLoader)

to :

def myClass = classLoader.loadClass("org.my.ServiceGenerator")

Still i found the java.lang.NoClassDefFoundError . I still couldn't figure out why this thing could happen. Can someone help me ?

I attach my GANT Script which calls up Service Generator

/**
 * Gant Script for generating Service Class based on Artifact
 * This Script was used for auto generate service class which is needed to handle basic transactional statements such as:
 * - AuditTrail for tables
 * - Logging before insert / update on tables
 */
includeTargets << grailsScript("_GrailsCreateArtifacts")
includeTargets << new File("${myPluginPluginDir}/scripts/GenerateExt.groovy")



target ('main': "Generates the CRUD service for a specified domain class") {
    depends(checkVersion, parseArguments, packageApp,classpath,configureProxy, 
            loadApp, configureApp, compile)

    promptForName(type: "Domain Class")
    generateViews = false
    generateForName = argsMap["params"][0]
    generateForOne()
}
setDefaultTarget(main)

Any help would be appreciated

Thanks


Solution

  • I finally done!

    This is an example of what you want to do: grails-flex-scaffold-script

    And this is my implementation:

    *change $userInterfacePluginDir for your plugin name

    Put this files in scripts:

    GenerateService.groovy

    includeTargets << grailsScript("_GrailsInit")
    includeTargets << grailsScript("_GrailsCreateArtifacts")
    includeTargets << new File("$userInterfacePluginDir/scripts/_GrailsGenerateService.groovy")
    
    target ('default': "Generates the service for a specified domain class") {
    depends(checkVersion, parseArguments, packageApp)
    promptForName(type: "Domain Class")
    generateForName = argsMap["params"][0]
    generateServiceForOne()
    }
    

    _GrailsGenerateService.groovy

    import org.codehaus.groovy.grails.commons.GrailsDomainClass;
    import org.codehaus.groovy.grails.scaffolding.*
    import org.springframework.core.io.AbstractResource;
    import org.springframework.core.io.FileSystemResource;
    import grails.util.GrailsNameUtils
    
    /**
     * Gant script that generates a service for a given domain class
     *
     * @author Martín Caballero
     *
     */
    
    includeTargets << grailsScript("_GrailsBootstrap")
    
    generateForName = null
    
    target(generateServiceForOne: "Generates service for only one domain class.") {
        depends(loadApp)
    
        def name = generateForName
        name = name.indexOf('.') > 0 ? name : GrailsNameUtils.getClassNameRepresentation(name)
        def domainClass = grailsApp.getDomainClass(name)
    
        if (!domainClass) {
            grailsConsole.updateStatus "Domain class not found in grails-app/domain, trying hibernate mapped classes..."
            bootstrap()
            domainClass = grailsApp.getDomainClass(name)
        }
    
        if (domainClass) {
            generateServiceForDomainClass(domainClass)
            event("StatusFinal", ["Finished generation for domain class ${domainClass.fullName}"])
        }
        else {
            event("StatusFinal", ["No domain class found for name ${name}. Please try again and enter a valid domain class name"])
            exit(1)
        }
    }
    
    def generateServiceForDomainClass(domainClass) {
        UserInterfaceTemplateGenerator = classLoader.loadClass('plugin.ui.scaffold.UserInterfaceTemplateGenerator')
        def templateGenerator = UserInterfaceTemplateGenerator.newInstance(classLoader)
        templateGenerator.grailsApplication = grailsApp
        templateGenerator.pluginManager = pluginManager
    
        event("StatusUpdate", ["Generating service for domain class ${domainClass.fullName}"])
        templateGenerator.generateService(domainClass, basedir)
        event("GenerateServiceEnd", [domainClass.fullName])
    }
    

    UserInterfaceTemplateGenerator: (helper class - extends DefaultGrailsTemplateGenerator)

    package plugin.ui.scaffold
    
    import org.codehaus.groovy.grails.scaffolding.DefaultGrailsTemplateGenerator;
    import grails.build.logging.GrailsConsole
    import grails.util.BuildSettingsHolder
    import groovy.text.SimpleTemplateEngine
    import groovy.text.Template
    import org.apache.commons.logging.Log
    import org.apache.commons.logging.LogFactory
    import org.codehaus.groovy.grails.commons.GrailsApplication
    import org.codehaus.groovy.grails.commons.GrailsDomainClass
    import org.codehaus.groovy.grails.plugins.GrailsPluginManager
    import org.codehaus.groovy.grails.plugins.PluginManagerAware
    import org.springframework.context.ResourceLoaderAware
    import org.springframework.core.io.ClassPathResource
    import org.springframework.core.io.FileSystemResource
    import org.springframework.core.io.ResourceLoader
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver
    import org.springframework.util.Assert
    import org.springframework.core.io.AbstractResource
    import org.codehaus.groovy.grails.scaffolding.SimpleDomainClassPropertyComparator
    import org.codehaus.groovy.grails.scaffolding.DomainClassPropertyComparator
    
    class UserInterfaceTemplateGenerator extends DefaultGrailsTemplateGenerator {
        public UserInterfaceTemplateGenerator(ClassLoader classLoader){
            super(classLoader)
        }
    
        void generateService(GrailsDomainClass domainClass, String destdir) {
            Assert.hasText destdir, "Argument [destdir] not specified"
    
            if (domainClass) {
                def fullName = domainClass.fullName
                def pkg = ""
                def pos = fullName.lastIndexOf('.')
                if (pos != -1) {
                    // Package name with trailing '.'
                    pkg = fullName[0..pos]
                }
    
                def destFile = new File("${destdir}/grails-app/services/${pkg.replace('.' as char, '/' as char)}${domainClass.shortName}Service.groovy")
                if (canWrite(destFile)) {
                    destFile.parentFile.mkdirs()
    
                    destFile.withWriter { w ->
                        generateService(domainClass, w)
                    }
    
                    LOG.info("Controller generated at ${destFile}")
                }
            }
        }
    
        void generateService(GrailsDomainClass domainClass, Writer out) {
            def templateText = getTemplateText("Service.groovy")
    
            boolean hasHibernate =pluginManager?.hasGrailsPlugin('hibernate')
            def binding = [pluginManager: pluginManager,
                           packageName: domainClass.packageName,
                           domainClass: domainClass,
                           className: domainClass.shortName,
                           propertyName: getPropertyName(domainClass),
                           comparator: hasHibernate ? DomainClassPropertyComparator : SimpleDomainClassPropertyComparator]
    
            def t = engine.createTemplate(templateText)
            t.make(binding).writeTo(out)
        }
    
        protected canWrite(File testFile) {
            if (!overwrite && testFile.exists()) {
                try {
                    def response = GrailsConsole.getInstance().userInput("File ${makeRelativeIfPossible(testFile.absolutePath, basedir)} already exists. Overwrite?",['y','n','a'] as String[])
                    overwrite = overwrite || response == "a"
                    return overwrite || response == "y"
                }
                catch (Exception e) {
                    // failure to read from standard in means we're probably running from an automation tool like a build server
                    return true
                }
            }
            return true
        }
    
        protected String getPropertyName(GrailsDomainClass domainClass) { "${domainClass.propertyName}${domainSuffix}" }
    
    }
    

    And, finally, this is my template: (Put it in src/templates/scaffolding/)

    Service.groovy

    <%import org.codehaus.groovy.grails.commons.GrailsClassUtils;%>
    <%=packageName ? "package ${packageName}\n\n" : ''%>
    class ${className}Service {
    
        def getTable() {
            def query = {
                <%
                    def searchFields = GrailsClassUtils.getStaticPropertyValue(domainClass.clazz, 'search' )
                    if(searchFields)
                    {
                %>
                        if (params.q) {
                            or{
                            <%searchFields.each { field ->%>
                                ilike("${field}", '%' + params.q + '%')
                            <%}%>
                            }
                        }
                <%  }   %>
                if (params.sort){
                    order(params.sort,params.order)
                }
            }
            def criteria = ${className}.createCriteria()
            return criteria.list(query, max: params.max, offset: params.offset)
        }
    }
    

    Then, you use the command as "grails generate-service yourdomainclass"

    Hope that helps!

    This is the essential of the answer:

    UserInterfaceTemplateGenerator = classLoader.loadClass('plugin.ui.scaffold.UserInterfaceTemplateGenerator')
    def templateGenerator = UserInterfaceTemplateGenerator.newInstance(classLoader)