Can I force a grails dev server to reload when any code recompiles?
compilation, grails, groovy, tomcat
Solution
CRITICAL UPDATE
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
- Watch the helper class.
- Refresh the service class `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.
- Install the plugin.
- Add the below `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"))
}
}
}
}
- PROBLEM STATEMENT should be fixed after the above steps followed.
NOTE
- This process might be resource intensive if followed for all classes and artefacts.
OLD & Valid Answer
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 the initial setup. (`run-app`)
- Hit the controller to see "Heisenberg"
- Modify `MyHelper.sayMyName()` to return "Walter White" instead of "Heisenberg"
- Hit the controller again and see "Walter White"
Observation:
- I would rather make `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()
}
}
- Now, referring to field in `MyHelper` in the service class will give an issue because the bean has been instantiated already in the container
Regarding 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
}
- Change the domain, add `String email`
- Save Domain class.
- Modify action to add `email` in `MyDomain` (obviously a new row will be created).
- Save Controller.
- Hit the action again.
- See the result.
Problem
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: - Grails automatically recompiles `Helper.groovy` - Grails swaps the new code into the running dev server - But the new `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!