The short question is: can I force the Grails 2.2 dev server to reload (reinitializing all singleton services, etc) whenever new code is hot-compiled and swapped in?
Let me make the question clear with an example. Let's say that
I have a singleton-scoped service called MyService
. In its @PostConstruct
function, it initializes a new object of type Helper
(and holds on to the reference).
I run-app
in dev mode, test things out, and then...
I edit the source code for Helper.groovy
What happens next is:
Helper.groovy
Helper
code isn't called because the already-instantiated MyService
object already has a handle on an instance of the old kind.My current workaround is to keep a dependency graph in my head, and touch
or trivially modify MyService
every time I change one of its dependencies. But I'd much rather force the dev server to reload completely whenever new code is swapped in.
So... is it possible to force the dev server to reload whenever any code changes?
And a bonus question: when I change a Domain Object, the only way I can force the dev server to "follow" those changes is to stop-app
, clean
, and run-app
manually. Can I automate this to happen whenever it's required?
Thanks!
I was able to get what you were looking for. Laying it down step wise:
REQUIREMENT -
When Helper
class is modified, service
class should be refreshed to refer to the modified helper class.
SETUP
//src/groovy
class SampleHelper {
String name = 'Heisenberg'
def sayMyName(){
"$name"
}
}
//grails-app/service
import javax.annotation.PostConstruct
class SampleService {
String cachedName
@PostConstruct
def initIt(){
cachedName = new SampleHelper().sayMyName()
}
def serviceMethod() {
cachedName
}
}
//controller
class SampleController {
def sampleService
def index() {
render sampleService.serviceMethod()
}
}
PROBLEM STATEMENT
When name
in helper class is updated to Gus
, the class refreshes but the service class is still referencing to the old instance of SampleHelper
. Result, name still shows Heisenberg
.
SOLUTION
onChange
of helper class.This can be achieved by using the Pluginator plugin in the application which gives the flexibility to watch a file and do something onChange
event.
ApplicationPlugin.groovy
under grails-app/conf
.as
class ApplicationPlugin {
//Watch the helper class
def watchedResources = "file:./src/groovy/**/*.groovy"
//Re-Register the bean (service class) onChange of Helper Class
//This can be generalized more.
def onChange = { event ->
if (event.source) {
def beans = beans {
sampleService(SampleService) { bean ->
bean.autowire = true
}
}
if (event.ctx) {
event.ctx.registerBeanDefinition(
"sampleService",
beans.getBeanDefinition("sampleService"))
}
}
}
}
NOTE
I am unable to recreate your problem in Grails 2.2.0. Below is the setup I have (correct me if I am wrong anywhere):
//src/groovy
class MyHelper{
def sayMyName(){
"Heisenberg"
}
}
//service
import javax.annotation.PostConstruct
class MyService {
def myHelper
@PostConstruct
def initIt(){
myHelper = new MyHelper()
}
def serviceMethod() {
myHelper.sayMyName()
}
}
//controller (to test)
class MyController {
def myService
def index() {
render myService.serviceMethod()
}
}
Steps:
run-app
)MyHelper.sayMyName()
to return "Walter White" instead of "Heisenberg"Observation:
MyHelper
a bean and use (inject) it in the service class for use.Entry to resources.groovy
as follows:
beans = {
myHelper(com.example.MyHelper)
}
Service class becomes:
class MyService {
def myHelper
def serviceMethod() {
myHelper.sayMyName()
}
}
MyHelper
in the service class will give an issue because the bean has been instantiated already in the containerRegarding Bonus Question:
I am unable to recreate the problem either in Grails 2.2.0
//domain
class MyDomain {
String name
}
//controller action
def add(){
def myDomain = new MyDomain(name: 'Testing').save(flush: true)
render myDomain.properties
}
String email
email
in MyDomain
(obviously a new row will be created).If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With