简体   繁体   中英

Creating an instance of a domain class inside a grails script

I am trying to create an instance of a domain class inside a grails 2.3.6 script:

def player = new Player(name:"Bob")
player.save()

But I keep getting an exception

java.lang.NoClassDefFoundError: gaming/Player

I've tried all the different bootstrapping tricks I've managed to find on the internet but they don't really change the result:

I've tried importing:

import gaming.Player

I've tried loading the bootstrap script:

includeTargets << grailsScript("_GrailsBootstrap")

I've tried depending on every task I managed to find:

depends(configureProxy, packageApp, classpath, loadApp, configureApp, compile, bootstrap)

I've even tried loading the class at runtime:

ApplicationHolder.application.getClassForName("gaming.Player")

Interestingly enough, this last line doesn't barf which suggests that grails can find my class, but chooses to ignore the find when I actually go to use it.

Edit. As requested, here is the current version of the script

import gaming.Player

import org.codehaus.groovy.grails.commons.ApplicationHolder

includeTargets << grailsScript("_GrailsInit")
includeTargets << grailsScript("_GrailsBootstrap")
includeTargets << grailsScript("_GrailsClasspath")

def handleHeaderLine(line) {
    def retval = []
    line.each {
        if(!it.equals("Game Name") && !it.equals("Total # of Copies")) {
            println("Creating Player: " + it)
            def player = new Player(name:it)
            player.save 
            retval << it
        } else {
            retval << null
        }
    }
    return retval;
}

def handleGameLine(header, line) {
    println("Creating Game: " + line[0])
    for(int i = 1; i < line.length - 1; i++) {
        if(!header[i].equals("Total # of Copies")) {
            def count = line[i] == "" ? 0 : Integer.parseInt(line[i]);
            for(int j = 0; j < count; j++) {
                println "Creating copy of " + line[0] + " owned by " + header[i]
            }
        }
    }
}

target(loadAssets: "The description of the script goes here!") {
    depends(configureProxy, packageApp, classpath, loadApp, configureApp, compile, bootstrap)

    ApplicationHolder.application.getClassForName("gaming.Player")

    def tsv = new File("...")

    def header = null;
    tsv.eachLine {
        def line = it.split("\t")
        if(header == null) {
            header = handleHeaderLine(line)
            println header
        } else {
            handleGameLine(header, line)
        }
    }
}

setDefaultTarget(loadAssets)

You do not have to do all the boiler plate effort to bring up the environment while running your script. run-script does that for you. When grails run-script is used following targets are run by default: checkVersion, configureProxy, bootstrap . And finally the script run-script is run.

run-script runs your custom script in GroovyShell by providing ApplicationContext and grailsApplication as bindings to shell. So what you would end up with your script is shown below as if it is written in Groovy console/shell:

//scripts/player/PlayerScript.groovy
def handleHeaderLine(line) {
    def retval = []
    line.each {
        if(!it.equals("Game Name") && !it.equals("Total # of Copies")) {
            println("Creating Player: " + it)
            def player = new Player(name: it)
            player.save(flush: true) 
            retval << it
        } else {
            retval << null
        }
    }
    return retval
}

def handleGameLine(header, line) {
    println("Creating Game: " + line[0])
    for(int i = 1; i < line.length - 1; i++) {
        if(!header[i].equals("Total # of Copies")) {
            def count = line[i] == "" ? 0 : Integer.parseInt(line[i]);
            for(int j = 0; j < count; j++) {
               println "Creating copy of " + line[0] + " owned by " + header[i]
            }
        }
    }
}

def tsv = new File("...")
def header = null
tsv.eachLine {
    def line = it.split("\t")
    if(header == null) {
        header = handleHeaderLine(line)
        println header
    } else {
        handleGameLine(header, line)
    }
}

And then use run-script as below:

grails run-script scripts/player/PlayerScript.groovy

which would by default run the script in dev environment. If you want for other envirnments then use as

grails test run-script scripts/player/PlayerScript.groovy

BUT
Due to a major bug in latest version of grails, you won't be able to run script the above mentioned way because run-script always depends on bootstrap target and would always try to bring tomcat up while running script as the plugin scope in build which would result in Error loading plugin manager: TomcatGrailsPlugin . The workaround is also mentioned in the defect but here is a groovier implementation. Change in BuildConfig.groovy as:

plugins {
    if ( !System.getProperty("noTomcat") ) {
        build ":tomcat:7.0.52.1"
    }
    ....
}

and then issue run-script command as:

 grails -DnoTomcat=true run-script scripts/player/PlayerScript.groovy

On a side note, the reason your script was not running is that the class Player will not be loaded at this time while running script, for use. It has to be loaded manually using classLoader and then create an instance off of it. Something like:

includeTargets << grailsScript("_GrailsInit")
includeTargets << grailsScript("_GrailsBootstrap")

target(playerScript: "The description of the script goes here!") {
    depends configureProxy, packageApp, classpath, loadApp, configureApp

    def playerClass = classLoader.loadClass("gaming.Player")

    //Skeptical about how a domain class would behave
    //But a normal POGO should be good being used this way
    def player = playerClass.newInstance([[name: "Bob"]] as Object[])
    player.save(flush: true)

    println player
}

setDefaultTarget(playerScript)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM