one-off grails script for populating database - grails

Update: as of Grails 1.3.6 one has access to the full domain from Gant scripts.
From the Grails 1.3.6 release notes:
You can now run one or more Groovy scripts from the commandline using the run-script command, e.g.
grails run-script [path-to-script-1] [path-to-script-2]...[path-to-script-n]
This works around the issue in Gant scripts where you can't conveniently access application classes since they're not available in the classpath when the scripts start.
Hi all,
I am new to using Grails (in a real project) and I have a one-off script I need to execute that reads a file and then populates my database.
I wanted the script to run in the context of my grails app, so I used the create-script command. I now understand that makes it a 'Gant' script. The reason for doing so was that I thought it would allow me easy access to all the grails domain good-ness, so that i would be able to do something like this easily:
Car car = new Car(model: 'bar', brand: 'Ford')
car.save()
Here, Car is one of my domain classes and the strings 'bar' and 'Ford' I have retrieved from my file.
The start of my script looks like this:
import com.foo.Car
grailsHome = Ant.project.properties."environment.GRAILS_HOME"
includeTargets << new File ( "${grailsHome}/scripts/Bootstrap.groovy" )
target(main: "a script for storing cars") {
depends(bootstrap, classpath) // code dealing with the file with cars follows
Surprisingly, groovy gives me a java.lang.NoClassDefFoundError: com.foo.Car when I execute the script with the command grails LoadCars
Am I taking the wrong approach, or is there something more simple I am doing wrong?
Any help is appreciated

i know the scripts are useful, and I will probably get hate mail for even suggesting it, but I have just incorporating this kinda of stuff directly into my application in the past.
I have a flag set in my configuration which indicates if the data should be bootstrapped, if so, the bootstrap code looks for a comma delimited file at startup and calls a service method to load up the data.

I've updated the grails run-script Gant script (referred to by Jared above) to work with grails 1.3.5. I'd been meaning to do it for a while, but this question nudged me into finally getting around to it).
Just download the script described in the post, save it in your grails "scripts" directory and you can then run your own groovy script to bootstrap data with:
grails run-script script-path/boostrapMyDataIntoApp.groovy

I've had to do this and you have to create a special script to allow you to access GORM from a standard grails script. See this question for more info. I'm not sure what the current status of the script is under grails 1.3 but the author of the script posted in the comments.

Hans, there are several choices here, assuming you are not out to polish the GANT scripting chops 8^)
So assume that you are doing some integration-mode TDD, correct?
Have you looked into the db-stuff plugin? Actually that one leverages the open source package (extension of the JUnit project) called dbUnit, which is also an outstanding choice, for both Java and Groovy projects.
*db-stuff <0.3.0> -- db schema managment and data import/export. Generate generic schema files and import or export base/seed/test data into your database.
I have traditionally done this as well in the BootStrap depending on the environment - and I try to never let those domain assumptions / constraints get too far out of synch. with my schema.
Here's the canon I'm talking about :
class BootStrap {
def init = { servletContext ->
if (GrailsUtil.environment.equals( GrailsApplication.ENV_DEVELOPMENT )) {
log.info( "Loading sample data for 2010 models..." );
new Car( manufacturer: new Manufacturer( name: "Toyota" ), model: "Prius" )
new Car( manufacturer: new Manufacturer( name: "GM" ), model: "Volt" )
//...

Related

Importing classes for use in grails script _Events.groovy

In Grails 2.3.7 I'm using _Events.groovy to hook into WAR packaging to do some special processing:
_Events.groovy
import demo.utils.XmlUtil
eventCreateWarStart = { name, stageDir ->
XmlUtil.doSomething()
...
log.debug('done!')
}
When building the WAR, Grails complains about XmlUtil import statement. _Events.groovy is not a class, so import statements don't work. How can I use a custom class in a script if I can't import it? And how can I perform logging instead of using println?
Update
Loading classes manually based on this and this seems to do the trick, also got logging to work thanks to Aaron's answer below:
eventCreateWarStart = { name, stageDir ->
def xmlUtil = loadRequiredClass('demo.utils.XmlUtil')
xmlUtil.doSomething()
...
grailsConsole.log('done!')
}
loadRequiredClass = {classname ->
classLoader.loadClass(classname)
}
Questions
What are all implicit objects available to Grails scripts?
It's a pain but it does make sense when you think about it. The _Events.groovy is part of the build process which is also responsible for compiling the classes that you are trying to use in _Events.groovy. Definitely a catch-22 scenario but I don't see how it could be made better without splitting _Events.groovy into separate files that compile and load at different stages of the build process.
You can use grailsConsole.log("hi") or grailsConsole.updateStatus("hi") to log output to the console.

Add logging to an external pluggable script

As described in Can't call one closure from another, I am using a pluggable script from within a Grails app.
Unfortunately, I've found that I can't use log4j from within these scripts. I am forced to use println.
I tried using
import org.apache.commons.logging.LogFactory
def Log log = LogFactory.getLog(getClass())
but I got no output. When I print out the result of the call to getClass(), I get something like
myscript$_run_closure5
So I'm thinking the issue is that there is no configuration in my Grails Config.groovy file for this class.
Is there a way for me to programmatically add these pluggable scripts to the log4j configuration? Keep in mind that I do not know in advance what the names of the scripts are, so this has to happen at runtime.
Consider the following code:
import org.apache.log4j.Logger
// ...
Logger log = Logger.getLogger('MyPlugin')
new File( grailsApplication.config.externalFiles ).eachFile { file ->
Binding binding = new Binding()
binding.variables.log = log
GroovyShell shell = new GroovyShell(binding)
shell.evaluate(file)
strategies.put( binding.variables.key, binding.variables )
}
Explanation:
It is not obligatory to pass class name to getLogger, it can be actually any string. You just need to make sure that this string is matched in log4j.properties of the main program.
You pass once created log to plugin scripts via binding variable "log". Then plugin scripts can access it simply as log.info('test123')
My personal recommendation would be to use logback instead of log4j. Both libraries were developed by the same guy and it is stated that logback supersedes log4j.

Grails default package name

I am new to Grails and I like it very much. I want to place my classes in packages like org.company.project.module.model. Its quite painful for to me to repeat create-domain-class <package>.<class_name>. Is there something like "package templates" or can I somehow "enter" (like grails cd org.comopany...) and then just write Class names (grails Person will be generated in ./ location)? Is that possible or should I use copy paste design pattern?
Thanks in advance for any help.
If I understood your question, you are looking for default package name for your domain classes. In your config.groovy file there is a line saying:
grails.project.groupId = appName
if you give it an appName, Grails will use that as the default package name when it generates the artifacts.
grails.project.groupId = 'com.example.yourpackagename'
If you now create a domain class by default it will locate it under com/example/yourpackagename.
UPDATE
It is not required to use Grails commands like create-domain-class or other commands to create artifacts. These are all just classes that you can manually create. Just create a file and duplicate it in the same package.
UPDATE
Grails interactive mode (when you type grails) for some of the commands by pressing tab it will type ahead the unique portion of the package name.
UPDATE for Grails 3.0
The setting has moved into conf/application.yml:
grails:
profile: web
codegen:
defaultPackage: com.example.yourpackagename

working with xtext not as plugin

In general I have my dsl as plugin and I want to create a new app that use my dsl
so i tried to write this code:
JsonParser p = new JsonParser();
IParseResult r = p.parse(new StringReader("{}"));
//once that work it will be the file data instead of {}
but when i do the parse the node model builder is null and the following line has exception:
return doParse(ruleName, in, nodeModelBuilder.get(), 0);
and i'm not sure how to init nodeModelBuilder
i'm sure i missing some steps but i'm not quite familiar with the xtext process.
thanks!
You already read the following answer on Eclipse Forum. You need to create an IParser instance by injecting it. All dependencies gets also injected. The necessary bindings are described in your JsonRuntimeModule. Xtext uses Guice and theses Modules to glue everything together. This pattern is called Dependency Injection.
... I want to create a new app that use my dsl
So you want to use your Json DSL in standalone mode.
My suggestion:
Create a minimum Eclipse IApplication with CLI that reads and parses an input file. The advantage of an Eclipse IApplication is that you can easily deploy an headless version of your DSL runtime. [1]
Have a look at your JsonInjectorProvider and the ParseHelper [2] from Xtext's JUnit support for examples how to use your DSL and Xtext runtime in standalone mode.
[1] http://www.eclipsezone.com/eclipse/forums/t99762.html
[2] org.eclipse.xtext.junit.util.ParseHelper
You are not supposed to call parser directly. See:
http://wiki.eclipse.org/Xtext/FAQ#How_do_I_load_my_model_in_a_standalone_Java_application.C2.A0.3F
The code should look like:
Injector injector = new MyDslStandaloneSetup().createInjectorAndDoEMFRegistration();
XtextResourceSet resourceSet = injector.getInstance(XtextResourceSet.class);
resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);
Resource resource = resourceSet.getResource(new File("/../../some.json").toURI(), true);
Model modelRootElement = (Model) resource.getContents().get(0);
Replace MyDsl with 'JsonParser' or 'Json' or whatever is your DSL name. Look for class JsonStandaloneSetup or JsonParserStandaloneSetup in your DSL source code. This class is generated when you start the Xtext project (or when you run workflow for the first time, not sure now). Replace Model with whatever is your root element type. It must be EObject subclass.
The parsing/validation/buidling AST is done resource.getContents() command. Not very intuitive, I know. It is because you have to initialize context, all sorts of contexts i fact, Guice context, EMF context, and perhaps other, all encapsulated in the StandaloneSetup (and RuntimeModule). The context is similar to Spring Application Context.
You need to use StandaloneSetup to run in standalone mode.
See this tutorial for help

Grails Events: Copying directories around at Build time to get a clean 'multi-tenant' deployment effect

I'm working on a sort of 'multi-tenant' grails app that will be used as a 'platform' upon which quick sites will be developed.
A 'site' will include a layout, images, and page gsps.
Right now, these are spread across the project in their normal locations, eg:
/grails-app/views/layout
/grails-app/views/<site>
/web-app/images/
Ideally, they'd all be in one place, centralized by site, like
/sites/<site>/layout
/sites/<site>/pages/
/sites/<site>/imagtes
My current thinking is this could be accomplished with a Build.groovy script and doing some ant trickery at build time.
But I'm not sure if it's possible to do this copying-by-convention - ie I don't know the directories that are present until it runs. (I'm also no ant guru)
Any ideas/suggestions? Thanks!
I have used the following script, named _Events.groovy and located in a scripts folder under your grails application, to copy files before my build:
includeTargets << grailsScript("_GrailsEvents")
eventSetClasspath = { msg ->
println "Custom Configuration"
ant.copy(todir:classesDirPath) {
fileset(dir:"${basedir}/config")
}
}
And I suppose you could use something very similar.
Relevant grails documentation is here

Resources