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.
Related
I have a Groovy application that I build with Gradle. As usual I have defined the application version number/string in the build.gradle script.
Now I want to use that version string within the Groovy application, as hard-coded static piece of information. For example as a final static member in the main application class:
class MyApp {
final static APP_VERSION = "0.1"
}
Since the version information comes from the build.gradle script, that Groovy class member above needs to be set by Gradle before the sources are compiled.
In other words: I need a Gradle task that allows me to set the value of a variable in the Groovy sources, before they are built by Gradle. I could for search for that value via regular expression and replace it in the Groovy source file, but that feels a bit clunky.
Any "best practice" ideas how to achieve that?
i can't give you final solution, but, i think, you should look at AST Transformations
Another option, if you pack the application in jar / war, - you can get a version of the application from the manifest file (but it will not work if you are starting app from IDE)
final static versin = MyApp.class.getPackage().getSpecificationVersion()
I just tried to read a grails config from groovy. The ConfigSlurper is easy to use, but since it executes the config, it needs all dependencies in place. In my case, it complains about a missing log4j class.
Even when I import this class into my ConfigSlurper script, the config itself runs into this problem.
Any Idea how I could make the log4j classes accessible to the config?
Update: now that I have a proper keyboard in front of me, I can elaborate on my problem:
I have grails configurations which configure log4j as described in the docs:
import org.apache.log4j.*
log4j = {
appenders {
appender new RollingFileAppender(
name: "myAppender",
maxFileSize: 1024,
file: "/tmp/logs/myApp.log"
)
}
}
Then I tried to parse them like this:
def file = new File(<config location>)
def config = new ConfigSlurper().parse(file.toURL())
and the Slurper threw an exception...
I've found two solutions:
First, the documents show a way how to configure a rollingFileAppender without instantiating one: http://grails.org/doc/2.3.7/guide/conf.html .
Second, I managed to fix my original problem with a
#Grab('log4j:log4j:')
in front of my script - not a good solution if you are behind a firewall...
As Jeff already stated, it should also be possible to put the right jar file in the classpath, but I have to say that I often struggle with the cp :-( and when I tried to put the log4j.jar in the .groovy folder, I didn't succeed - but I guess this was because of an inproper groovy installation.
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.
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
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" )
//...