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.
Related
I have a project built using Spring Boot. In this project I have included the gorm-hibernate4-spring-boot plugin in order to use Grails' GORM object mapping sugar. When running the project, GORM does its thing without issue and everything is great. Tests, on the other hand, are another matter. In a Grails project I would need to annotate my test cases with #Mock([DomainOne, DomainTwo]). The GORM plugin for spring boot works differently.
An answer for another question around Spring Boot and GORM linked to HibernateMixin from the grails-datastore-test-support plugin. In order to use this mixin, a project must also have the grails #TestMixin annotation which is included in an artifact that pulls in the rest of Grails. This same answer also suggested use of HibernateDatastoreSpringInitializer in order to initialize GORM in a spec's setup() method. Unfortunately I could not get this to work despite seeming to be the best bet.
Here's my build.gradle:
buildscript { // Defines dependencies and repos for the build script itself
repositories { mavenCentral() }
dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.7.RELEASE") }
}
apply plugin: 'groovy'
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'spring-boot'
version = 1.0
mainClassName='foo.bar.Baz'
repositories {
mavenCentral()
maven { url 'http://repo.spring.io/libs-milestone' }
maven { url 'http://repo.spring.io/libs-snapshots' }
flatDir { dirs 'lib' }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-jdbc") {
exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
}
compile("org.apache.activemq:activemq-broker") // ActiveMQ
compile('commons-io:commons-io:2.4') // Commons IO
compile("org.springframework.boot:spring-boot-starter-log4j") // log 4j
compile('org.codehaus.groovy:groovy-all')
compile('org.postgresql:postgresql:9.4-1201-jdbc41') // JDBC
compile('log4j:log4j:1.2.17') // Default logging provider
compile("org.grails:gorm-hibernate4-spring-boot:5.0.0.RC2") // I summon thee, gorm
testCompile("org.spockframework:spock-core:0.7-groovy-2.0")
testCompile("org.springframework.boot:spring-boot-starter-test")
testCompile('com.h2database:h2:1.3.148') // h2 for GORM use during tests
}
task wrapper(type: Wrapper) {
gradleVersion = '2.3'
}
Here's my test case:
class DataTypeValidatorSpec extends Specification {
def setup() {
// Fails with "org.hibernate.HibernateException: No Session found for current thread"
def datastoreInitializer = new HibernateDatastoreSpringInitializer(MyDomainClass)
def applicationContext = new GenericApplicationContext()
def dataSource = new DriverManagerDataSource(
"jdbc:h2:mem:grailsDb1;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_DELAY=-1",
'sa', ''
)
dataSource.driverClassName = Driver.name
applicationContext.beanFactory.registerSingleton("dataSource", dataSource)
datastoreInitializer.configureForBeanDefinitionRegistry(applicationContext)
applicationContext.refresh() // is this needed?
}
void "should do gorm stuff"() {
given:
MyDomainClass instance = new MyDomainClass(name: 'foo').save()
when:
MyDomainClass locatedInstance = MyDomainClass.findByName('foo')
then:
instance == locatedInstance
}
}
I run the tests with gradle test. The code in the setup method for my spec is a pretty direct translation of the setup used in the HibernateDatastoreSpringInitializerSpec linked above.
Note, this is not a Grails project and if possible I'd like to keep the Grails related code to a minimum. Thanks in advance!
I was not able to get mocking to work without dragging in Grails core. I just removed GORM and went with Spring JPA / Hibernate based entities and repositories. These are easy to use with Spock and provide a more visible code flow for persisting, updating, etc. Sorry GORM.
This is a Spring Boot application so use the Boot integration testing annotations:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
If you use these GORM will be loaded using the mechanism provided by gorm-hibernate4-spring-boot
My requirement is to invoke some processing from a Jenkins build server, to determine whether the domain model has changed since the last build. I've come to the conclusion that the way forward is to write a script that will invoke a sequence of existing scripts from the db-migration plugin. Then I can invoke it in the step that calls test-app and war.
I've looked in the Grails doc, and at some of the db-migration scripts, and I find I'm stuck - have no idea where to start trying things. I'd be really grateful if someone could point me at any suitable sources. BTW, I'm a bit rusty in Grails. Started to teach myself two years ago via proof of concept project, which lasted 6 months. Then it was back to Eclipse rich client work. That might be part of my problem, though I never go involved in scripts.
One thing I need in the Jenkins evt is to get hold of the current SVN revision number being used for the build. Suggestions welcome.
Regards, John
Create a new script by running grails create-script scriptname. The database-migration plugins scripts are configured to be easily reused. There are is a lot of shared code in _DatabaseMigrationCommon.groovy and each script defines one target with a unique name. So you can import either the shared script or any standalone script (or multiple scripts) and call the targets like they're methods.
By default the script generated by create-script "imports" the _GrailsInit script via includeTargets << grailsScript("_GrailsInit") and you can do the same, taking advantage of the magic variables that point at installed plugins' directories:
includeTargets << new File("$databaseMigrationPluginDir/scripts/DbmGenerateChangelog.groovy")
If you do this you can remove the include of _GrailsInit since it's already included, but if you don't that's fine since Grails only includes files once.
Then you can define your target and call any of the plugin's targets. The targets cannot accept parameters, but you can add data to the argsMap (this is a map Grails creates from the parsed commandline arguments) to simulate user-specified args. Note that any args passed to your script will be seen by the database-migration plugin's scripts since they use the same argsMap.
Here's an example script that just does the same thing as dbm-generate-changelog but adds a before and after message:
includeTargets << new File("$databaseMigrationPluginDir/scripts/DbmGenerateChangelog.groovy")
target(foo: "Just calls dbmGenerateChangelog") {
println 'before'
dbmGenerateChangelog()
println 'after'
}
setDefaultTarget foo
Note that I renamed the target from main to foo so it's unique, in case you want to call this from another script.
As an example of working with args, here's a modified version that specifies a default changelog name if none is provided:
println 'before'
if (!argsMap.params) {
argsMap.params = ['foo2.groovy']
}
dbmGenerateChangelog()
println 'after'
Edit: Here's a fuller example that captures the output of dbm-gorm-diff to a string:
includeTargets << new File("$databaseMigrationPluginDir/scripts/_DatabaseMigrationCommon.groovy")
target(foo: "foo") {
depends dbmInit
def configuredSchema = config.grails.plugin.databasemigration.schema
String argSchema = argsMap.schema
String effectiveSchema = argSchema ?: configuredSchema ?: defaultSchema
def realDatabase
boolean add = false // booleanArg('add')
String filename = null // argsList[0]
try {
printMessage "Starting $hyphenatedScriptName"
ByteArrayOutputStream baos = new ByteArrayOutputStream()
def baosOut = new PrintStream(baos)
ScriptUtils.executeAndWrite filename, add, dsName, { PrintStream out ->
MigrationUtils.executeInSession(dsName) {
realDatabase = MigrationUtils.getDatabase(effectiveSchema, dsName)
def gormDatabase = ScriptUtils.createGormDatabase(dataSourceSuffix, config, appCtx, realDatabase, effectiveSchema)
ScriptUtils.createAndPrintFixedDiff(gormDatabase, realDatabase, realDatabase, appCtx, diffTypes, baosOut)
}
}
String xml = new String(baos.toString('UTF-8'))
def ChangelogXml2Groovy = classLoader.loadClass('grails.plugin.databasemigration.ChangelogXml2Groovy')
String groovy = ChangelogXml2Groovy.convert(xml)
// do something with the groovy or xml here
printMessage "Finished $hyphenatedScriptName"
}
catch (e) {
ScriptUtils.printStackTrace e
exit 1
}
finally {
ScriptUtils.closeConnection realDatabase
}
}
setDefaultTarget foo
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.
I have a pretty standard 2.0.3 Grails app and I've executed grails install-templates which places files list.gsp, edit.gsp, etc. in src/templates/scaffolding/ directory. These files are not automatically reloaded when a change is made to them. Is there a way I can get these to be automatically reloaded so I don't have to stop/start the app every time I make a change? I've tried looking at watchedResources but that seems to be plugin development related.
You are correct that the "watched-resources" mechanism only applies to plugins. The correct fix for this would be to modify the core ScaffoldingGrailsPlugin.groovy to add
def watchedResources = "file:./src/templates/scaffolding/*"
and it's probably worth submitting a JIRA to that effect. In the mean time, you may be able to get it working by writing a simple plugin of your own to "inject" this behaviour into the scaffolding plugin. Do grails create-plugin watch-scaffolding and then use the following for the plugin descriptor:
import org.codehaus.groovy.grails.plugins.GrailsPlugin
class WatchScaffoldingGrailsPlugin {
def version = "0.1"
def grailsVersion = "2.0 > *"
def dependsOn = [:]
def pluginExcludes = [ "grails-app/views/error.gsp" ]
def title = "Watch Scaffolding Plugin"
def author = "Your name"
def authorEmail = ""
def description = '''\
Watches for changes to scaffolding templates and reloads dynamically-scaffolded
controllers and views.
'''
// URL to the plugin's documentation
def documentation = "http://grails.org/plugin/watch-scaffolding"
// watch for changes to scaffolding templates...
def watchedResources = "file:./src/templates/scaffolding/*"
// ... and kick the scaffolding plugin when they change
def onChange = { event ->
event.manager.getGrailsPlugin('scaffolding').notifyOfEvent(
GrailsPlugin.EVENT_ON_CHANGE, null)
}
// rest of plugin options are no-op
def onConfigChange = { event -> }
def doWithWebDescriptor = { xml -> }
def doWithSpring = { }
def doWithDynamicMethods = { ctx -> }
def doWithApplicationContext = { applicationContext -> }
def onShutdown = { event -> }
}
Now in your application's BuildConfig.groovy add
grails.plugin.location.'watch-scaffolding' = '../watch-scaffolding'
(or whatever is the appropriate relative path from the root of your app to the root of the plugin) and your scaffolding template changes should start to reload automatically.
(This is tested on Grails 2.1, I initially tried using influences but it didn't have any effect, however forcing an onChange event in the scaffolding plugin had the required result.)
This code flushes scaffolding cache. You can create a specific admin action for it:
org.codehaus.groovy.grails.scaffolding.view.
ScaffoldingViewResolver.scaffoldedViews.clear()
According to GRAILS-755, this has been fixed, but I don't think it has because they don't reload for me either.
From that Jira, here is a possible workaround:
Use the console plugin, and run this command to clear the dynamic
scaffolded view cache:
def scaffoldedView =
org.codehaus.groovy.grails.scaffolding.view.ScaffoldingViewResolver.scaffoldedViews.clear()
After that, the next time I request a page, it doesn't find it in the
cache, and thus goes back to the disk to recreate it.
I'm developing my first Grails plugin. It has to access a webservice. The Plugin will obviously need the webservice url. What is the best way to configure this without hardcoding it into the Groovy classes? It would be nice with different config for different environments.
You might want to Keep It Simple(tm). You may define the URL directly in Config.groovy -including per-environment settings- and access it from your plugin as needed using grailsApplication.config (in most cases) or a ConfigurationHolder.config object (See further details in the manual).
As an added bonus that setting may also be defined in standard Java property files or on other configuration files specified in grails.config.locations.
e.g. in Config.groovy
// This will be the default value...
myPlugin.url=http://somewhe.re/test/endpoint
environments {
production {
// ...except when running in production mode
myPlugin.url=http://somewhe.re/for-real/endpoint
}
}
later, in a service provided by your plugin
import org.codehaus.groovy.grails.commons.ConfigurationHolder
class MyPluginService {
def url = ConfigurationHolder.config.myPlugin.url
// ...
}
If its only a small (read: one item) config option, it might just be easier to slurp in a properties file. If there are some number of configuration options, and some of them should be dynamic, i would suggest doing what the Acegi Security plugin does - add a file to /grails-app/conf/plugin_name_config.groovy perhaps.
added bonus is that the user can execute groovy code to compute their configuration options (much better over using properties files), as well as being able to do different environments with ease.
check out http://groovy.codehaus.org/ConfigSlurper , which is what grails internally use to slurp configs like config.groovy.
//e.g. in /grails-app/conf/MyWebServicePluginConfig.groovy
somePluginName {
production {
property1 = "some string"
}
test {
property1 = "another"
}
}
//in your myWebServicePlugin.groovy file, perhaps in the doWithSpring closure
GroovyClassLoader classLoader = new GroovyClassLoader(getClass().getClassLoader())
ConfigObject config
try {
config = new ConfigSlurper().parse(classLoader.loadClass('MyWebServicePluginConfig'))
} catch (Exception e) {/*??handle or what? use default here?*/}
assert config.test.property1.equals("another") == true