I've created a Grails plugin which adds a custom test type class (extending GrailsTestTypeSupport) and custom test result class (extending GrailsTestTypeResult) to support a custom test type that I run during the other phase of the test-app script. Testing this on my local machine has gone swimmingly but...
When I packaged the plugin to use in my app, the tests are blowing up on our CI server (Jenkins). Here's the error that Jenkins is spitting out:
unable to resolve class CustomTestResult # line 58, column 9.
new CustomTestResult(tests.size() - failed, failed)
It appears that I cannot simply import these classes into _Events.groovy, and the classes are not otherwise on the classpath. But I'll be damned if I can figure out how to get them onto the classpath. Here's what I have so far (in _Events.groovy):
import java.lang.reflect.Constructor
eventAllTestsStart = {
if (!otherTests) otherTests = []
loadCustomTestResult()
otherTests << createCustomTestType()
}
private def createCustomTestType(String name = 'js', String relativeSourcePath = 'js') {
ClassLoader parent = getClass().getClassLoader()
GroovyClassLoader loader = new GroovyClassLoader(parent)
Class customTestTypeClass = loader.parseClass(new File("${customTestPluginDir}/src/groovy/custom/test/CustomTestType.groovy"))
Constructor customTestTypeConstructor = customTestTypeClass.getConstructor(String, String)
def customTestType = customTestTypeConstructor.newInstance(name, relativeSourcePath)
customTestType
}
private def loadCustomTestResult() {
ClassLoader parent = getClass().getClassLoader()
GroovyClassLoader loader = new GroovyClassLoader(parent)
Class customTestResultClass = loader.parseClass(new File("${customTestPluginDir}/src/groovy/custom/test/CustomTestResult.groovy"))
}
Currently: CustomTestResult is only referenced from within CustomTestType. As far as I can tell, _Events.groovy is loading CustomTestType but it is failing because it then insists that CustomTestResult is not on the classpath.
Putting aside for a moment that it seems crazy that there's this much overhead to get plugin-furnished classes onto the classpath for the test cycle to begin with... I'm not quite sure where I've gotten tripped up. Any help or pointers would be greatly appreciated.
Have you tried simply loading the class in question via the ClassLoader that is accessible via the classLoader variable in _Events.groovy?
Class customTestTypeClass = classLoader.loadClass('custom.test.CustomTestType')
// use nice groovy overloading of Class.newInstance
return customTestTypeClass.newInstance(name, relativeSourcePath)
You should be late enough in the process at eventAllTestsStart for this to be valid.
#Ian Roberts' answer got me pointed in roughly the right direction, and combined with the _Events.groovy script from this grails-cucumber plugin, I managed to come through with this solution:
First, _Events.groovy became this:
eventAllTestsStart = { if (!otherTests) otherTests = [] }
eventTestPhasesStart = { phases ->
if (!phases.contains('other')) { return }
// classLoader.loadClass business per Ian Roberts:
otherTests << classLoader.loadClass('custom.test.CustomTestType').newInstance('js', 'js')
}
Which is far more readable than where I was at the start of this thread. But: I was in roughly the same position: my ClassNotFoundException moved from being thrown in _Events.groovy to being thrown from within CustomTestType when it tried to create an instance of custom.test. CustomTestResult. So within CustomTestType, I added the following method:
private GrailsTestTypeResult createResult(passed, failed) {
try {
return new customTestResult(passed, failed)
} catch(ClassNotFoundException cnf) {
Class customTestResult = buildBinding.classLoader.loadClass('custom.test.CustomTestResult')
return customTestResult.newInstance(passed, failed)
}
}
So Ian was right, inasmuch as classLoader came to the rescue -- I just wound up needing its magic in two places.
Related
developing application using Grails 2.5.1 i used Quartz plugin , and created a job successfully , but when i inject a service in this job i get org.quartz.JobExecutionException: java.lang.NullPointerException
here is the Job's code:
class EveryMonthJob {
def usersUtilsService
static triggers = {
cron name: 'EveryOneMonthJob', cronExpression: "* 31 2 L * ?"
}
def execute() {
usersUtilsService.testMe() // getting the exception here
}
}
There are any number of reasons that might not work. If you are creating an instance of the job yourself (as opposed to Spring creating the instance and subjecting it to dependency injection), that would explain why the reference is null. Another explanation could be that you have the property name wrong.
See the project at https://github.com/jeffbrown/sherifquestion. That is a Grails 2.5.1 app that does just what you are describing and it works fine. See https://github.com/jeffbrown/sherifquestion/blob/e0179f836314dccb5f83861ae8466bfd99717995/grails-app/jobs/demo/EveryMonthJob.groovy which looks like this:
class EveryMonthJob {
// generally I would statically type this property but
// am leaving it dynamically typed top be consistent with
// a question being asked...
def usersUtilsService
static triggers = {
simple repeatInterval: 5000l // execute job once in 5 seconds
}
def execute() {
usersUtilsService.testMe() // this works fine
}
}
First, I came from a .NET background so please excuse my lack of groovy lingo. Back when I was in a .NET shop, we were using TypeScript with C# to build web apps. In our controllers, we would always receive/respond with DTOs (data xfer objects). This got to be quite the headache every time you create/modify a DTO you had to update the TypeScript interface (the d.ts file) that corresponded to it.
So we created a little app (a simple exe) that loaded the dll from the webapp into it, then reflected over it to find the DTOs (filtering by specific namespaces), and parse through them to find each class name within, their properties, and their properties' data types, generate that information into a string, and finally saved as into a d.ts file.
This app was then configured to run on every build of the website. That way, when you go to run/debug/build the website, it would update your d.ts files automatically - which made working with TypeScript that much easier.
Long story short, how could I achieve this with a Grails Website if I were to write a simple groovy app to generate the d.ts that I want?
-- OR --
How do I get the IDE (ex IntelliJ) to run a groovy file (that is part of the app) that does this generation post-build?
I did find this but still need a way to run on compile:
Groovy property iteration
class Foo {
def feck = "fe"
def arse = "ar"
def drink = "dr"
}
class Foo2 {
def feck = "fe2"
def arse = "ar2"
def drink = "dr2"
}
def f = new Foo()
def f2 = new Foo2()
f2.properties.each { prop, val ->
if(prop in ["metaClass","class"]) return
if(f.hasProperty(prop)) f[prop] = val
}
assert f.feck == "fe2"
assert f.arse == "ar2"
assert f.drink == "dr2"
I've been able to extract the Domain Objects and their persistent fields via the following Gant script:
In scripts/Props.groovy:
import static groovy.json.JsonOutput.*
includeTargets << grailsScript("_GrailsBootstrap")
target(props: "Lists persistent properties for each domain class") {
depends(loadApp)
def propMap = [:].withDefault { [] }
grailsApp.domainClasses.each {
it?.persistentProperties?.each { prop ->
if (prop.hasProperty('name') && prop.name) {
propMap[it.clazz.name] << ["${prop.name}": "${prop.getType()?.name}"]
}
}
}
// do any necessary file I/O here (just printing it now as an example)
println prettyPrint(toJson(propMap))
}
setDefaultTarget(props)
This can be run via the command line like so:
grails props
Which produces output like the following:
{
"com.mycompany.User": [
{ "type": "java.lang.String" },
{ "username": "java.lang.String" },
{ "password": "java.lang.String" }
],
"com.mycompany.Person": [
{ "name": "java.lang.String" },
{ "alive": "java.lang.Boolean" }
]
}
A couple of drawbacks to this approach is that we don't get any transient properties and I'm not exactly sure how to hook this into the _Events.groovy eventCompileEnd event.
Thanks Kevin! Just wanted to mention, in order to get this to run, here are a few steps I had to make sure to do in my case that I thought I would share:
-> Open up the grails BuildConfig.groovy
-> Change tomcat from build to compile like this:
plugins {
compile ":tomcat:[version]"
}
-> Drop your Props.groovy into the scripts folder on the root (noting the path to the grails-app folder for reference)
[application root]/scripts/Props.groovy
[application root]/grails-app
-> Open Terminal
gvm use grails [version]
grails compile
grails Props
Note: I was using Grails 2.3.11 for the project I was running this on.
That gets everything in your script to run successfully for me. Now to modify the println portion to generate Typescript interfaces.
Will post a github link when it is ready so be sure to check back.
I created a Grails plugin (grails create-plugin myplugin1) and noticed that there was no myapp1/grails-app/conf/BootStrap.groovy created like you normally get when you create a Grails app.
I created one like so:
class BootStrap {
def init = {
println("Hello! The plugin is bootstrapping...")
}
def destroy = {
}
}
I then include the plugin with a Grails app (by adding myplugin1 as a plugin inside the app's BuildConfig.groovy). When I issue a grails run-app, I don't see the above println ever executing.
Do Grails plugins not use BootStrap.groovy? If so, where should I put "bootstrap" code that needs to be executed when a plugin is loaded? Otherwise, if I was correct to do this above, why might I not be seeing the "Hello! The plugin is bootstrapping..." message print out?
As always, start with the very well written and maintained documentation.
Plugins do not include Bootstrap.groovy. The following are excluded from plugins (taken from documentation).
grails-app/conf/BootStrap.groovy
grails-app/conf/BuildConfig.groovy (although it is used to generate dependencies.groovy)
grails-app/conf/Config.groovy
grails-app/conf/DataSource.groovy (and any other *DataSource.groovy)
grails-app/conf/UrlMappings.groovy
grails-app/conf/spring/resources.groovy
Everything within /web-app/WEB-INF
Everything within /web-app/plugins/**
Everything within /test/**
SCM management files within /.svn/ and /CVS/
In order to run code on startup of your plugin you need to hook into the runtime configuration of your plugin by using the doWithSpring or doWithApplicationContext (depending on what you need to do).
All of this, and more, is explained in the documentation. An example might be:
// MyFancyPlugin.groovy
...
def doWithApplicationContext = { appCtx ->
def sessionFactory = appCtx.sessionFactory
// do something here with session factory
}
...
Plugin's BootStrap is EXCLUDED from the plugin package. You have to do your init-phase in the plugin descriptor, in one or several of the following closures:
def doWithSpring = {
def appName = application.metadata.'app.name'
}
def doWithDynamicMethods = { ctx ->
// TODO Implement registering dynamic methods to classes (optional)
}
def doWithApplicationContext = { applicationContext ->
// TODO Implement post initialization spring config (optional)
}
Code that needs to run at startup time should go in the doWithApplicationContext closure in the plugin descriptor (MyPlugin1GrailsPlugin.groovy).
Alternatively, call it something else (e.g. MyPluginBootStrap.groovy), as it's only the specific classes BootStrap and UrlMappings that are excluded when a plugin is packaged, but any class whose name ends BootStrap is considered a bootstrap artefact.
I have a singleton class
#Singleton
class CustomerBundleSingleton {
def grailsApplication = Holders.getGrailsApplication()
String projName
private CustomerBundleSingleton() {
line 10: projName = // how to get sub-project name here ???
}
}
application.properties // my project is running
-----------------------
app.name = MyNewProject
application.properties // located in sub project
-----------------------
app.name = MySubProject
I tried grailsApplication.metadata['app.name'] in "line 10:" it returns "MyNewProject".Whereas I want a way to get the project name of the UserBundleSingleton located (MySubProjectName). Something like grailsApplication.current.metadata['app.name'] ???? .
So that it can give me back MySubProjectName instead of MyNewProject??
I have 3 suggestions depending on your requirements and your 'bundling'.
1) You don't have a bundle marker/descriptor
Assuming that you know the sub-project(Grails plugin) name, your life gets easier, instead of having to loop through all plugins...
You can probably use something among these lines.
// Plugin name is 'hibernate' in this example
import org.codehaus.groovy.grails.plugins.PluginManagerHolder
def hibernateVersion = PluginManagerHolder.pluginManager.getGrailsPlugin('hibernate').version
// Loop through all plugins
// PluginManagerHolder.pluginManager.getAllPlugins()
2) Using custom plugin properties to lookup plugins of interest
Other strategy, if you must lookup the bundle dynamically.
Create a custom marker property in each of your plugin descriptors
def specialProperty = "whatever"
Then inside your CustomerBundleSingleton
PluginManagerHolder.pluginManager.getAllPlugins().each {
if (it.properties.specialProperty) {
def subProjectName = it.name
def subProjectVersion = it.version
}
}
3) Custom bundle info resolution
You may also want to consider some metadata via META-INF/MANIFEST.MF or similar mechanism.
Hope it helps...
Well, it seems a simple task but I didn't manage to make it run.
I have a groovy script that runs fine under Windows Vista when calling from prompt:
> cd MY_GAILS_PROJECT_DIR
> groovy cp src/groovy scripts/myscript.groovy
Now, I want to execute this script (and passing to it some input arguments) through my my Maintenance Service Class (called from a controller) as below,
class MaintenanceService {
def executeMyScript() {
"groovy cp src/groovy scripts/myscript.groovy".execute()
}
}
It does not work at all! I don't even manage to have the execute() method recognizing any command (like "cd .".execute()) throwing exception:
Error 500: java.io.IOException: Cannot run program "cd": CreateProcess error=2, The system cannot find the file specified
1- How can I execute a groovy script from my grails application?
2- What are the best practices here? For instance, should I use the QuartzPlugin and then the triggerNow method for executing a script? should I use a Gant Task? If yes, how to do it?
Thank you.
If you don't mind your script running asynchronously (in a separate process to the service method), the following should work assuming groovy is on your PATH variable:
def cmd = ['groovy.bat', 'cp', 'src/groovy scripts/myscript.groovy']
cmd.execute()
If you want to view the output of the process in the application console, you should try something like this instead
// Helper class for redirecting output of process
class StreamPrinter extends Thread {
InputStream inputStream
StreamPrinter(InputStream is) {
this.inputStream = is
}
public void run() {
new BufferedReader(new InputStreamReader(inputStream)).withReader {reader ->
String line
while ((line = reader.readLine()) != null) {
println(line)
}
}
}
}
// Execute the script
def cmd = ['groovy', 'cp', 'src/groovy scripts/myscript.groovy']
Process executingProcess = cmd.execute()
// Read process output and print on console
def errorStreamPrinter = new StreamPrinter(executingProcess.err)
def outputStreamPrinter = new StreamPrinter(executingProcess.in)
[errorStreamPrinter, outputStreamPrinter]*.start()
Update:
In response to your comment below, try the following (which assumes you're on Windows):
1: Create the file C:\tmp\foo.groovy. The content of this file should be simply:
println 'it works!'
2: In the groovy console, run the following:
cmd = ['groovy.bat', 'C:\\tmp\\foo.groovy']
cmd.execute().text
3: You should see the result of the script (the text 'it works!') shown in the Groovy console
If you can't get this simple example working, there's something wrong with your environment, e.g. 'groovy.bat' is not on your PATH. If you can get this example working, then you should be able to work forward from it to achieve your objective.
As of grails 1.3.6 the run-script command is built in to let you run
grails run-script myScript.groovy
For earlier versions of grails, check out my updated blog post from what Carlos posted above.
Easiest Way:
Generate an Groovy Class and place at in your /src/groovy Folder of your Grails Project.
Import that Class in your Domain Class and use the Functions you defined.
My 2 Cents...
This might help as well:
http://naleid.com/blog/2008/03/31/using-gant-to-execute-a-groovy-script-within-the-grails-context-updated/
Carlos
Another decision you can use GroovyScriptEngine for example:
file MyScript.groovy:
static String showMessage() {
println("Message from showMessage")
}
file BootStrap.groovy:
class BootStrap {
def init = { servletContext ->
new GroovyScriptEngine("scripts")
.loadScriptByName("MyScript.groovy")
.showMessage()
}
def destroy = {
}
}