I have a Grails service class that needs to do some cleanup when my Tomcat application server is shut down.
I don't see anything in the Grails docs about a service.stop() or destroy() method, or a way to implement any sort of application lifecycle listener.
What's the best way to do this?
Thanks!
You have a couple of options
Make your service implement org.springframework.beans.factory.DisposableBean
class MyService implements org.springframework.beans.factory.DisposableBean {
void destroy() throws Exception {
}
}
Or use an annotation
class MyService {
#PreDestroy
private void cleanUp() throws Exception {
}
}
IMO, the annotation option is preferable, because you can give your destructor method a more meaningful name than destroy and your classes public API doesn't expose the Spring dependency
The grails-app/conf/BootStrap.groovy can be used when the app starts and stops.
def init = {
println 'Hello World!'
}
def destroy = {
println 'Goodnight World!'
}
Note: When using development mode grails run-app on some OS's CTL+C will kill the JVM without the chance for a clean shutdown and the destroy closure may not get called. Also, if your JVM gets the kill -9 the closure wont run either.
I would try injecting the service into the Bootstrap and then calling the method from the destroy block, since the destroy block is executed when the application is terminated, something like this:
class BootStrap {
def myService
def init = {servletContext ->
}
def destroy = {
myService.cleanUp()
}
}
It's not quite the same as a service disposal method, but what I ended up doing is registering a Spring Bean with a shutdown method that gets called when the app is stopped.
First, create a bean class, like grails-app/utils/MyShutdownBean.groovy that looks like the following (there's nothing sacred about the class name or the method name, use whatever you want):
class MyShutdownBean {
public void cleanup() {
// Do cleanup stuff
}
}
Then register the bean in grails-app/conf/spring/resources.groovy like this:
beans = {
myShutdownHook(MyShutdownBean) { bean ->
bean.destroyMethod='cleanup'
}
}
If you want to only do the cleanup in production, you can register it like this instead:
beans = {
if (!grails.util.GrailsUtil.isDevelopmentEnv()) {
myShutdownHook(MyShutdownBean) { bean ->
bean.destroyMethod='cleanup'
}
}
}
Related
I have a service with a PostConstruct annotated init method to ensure that it does not run until dependency injection has completed.
#PostConstruct
private void init() {
// Create some datasources on the fly
MyDomain.list().each {
createDataSource(it)
}
}
I now have a need to inject this service into taglib, but the application will not boot with the following error..
java.lang.IllegalStateException: Method on class [com.me.MyDomain] was
used outside of a Grails application
The stacktrace specifically points to the usage above and of course it boots fine when I remove.
Does anyone know of a 'credible' way around this?
try like:
#PostConstruct
void init() {
Domain.withNewSession {
println Domain.count
}
}
or
#PostConstruct
void init() {
Domain.withTransaction {
println Domain.count
}
}
Grails 2.4.5 here. I did a grails create-service com.example.service.Simple which created a SimpleService for me, which I then modified to look like so:
class SimlpeService {
SimpleClient simpleClient
Buzz doSomething(String derp) {
// ...
Fizz fizz = simpleClient.doSomething(derp)
// ...
fizz.asBuzz()
}
}
I can now "inject" controllers with SimpleService like so:
class MyController {
SimpleService simpleService
def index() {
// etc...
}
}
But how do I configure/wire SimpleService with the correct SimpleClient instance. Let's pretend SimpleClient is typically built like so:
SimpleClient simpleClient = SimpleClientBuilder
.withURL('http://simpleclientdev.example.com/simple')
.withAuth('luggageCombo', '12345')
.isOptimized(true)
.build()
Depending on what environment I'm in, I may want my SimpleClient instance to connect to simpleclientdev.example.com, simpleclientqa.example.com, or even simpleclient.example.com. Also, I may use different auth credentials, and I might/might not want it to be "optimized", etc. The point is: How do I inject the SimpleService with a SimpleClient instance?
You can use the Java's PostConstruct annotation on one of your method in your service to do the stuff you want. From the docs:
The PostConstruct annotation is used on a method that needs to be
executed after dependency injection is done to perform any
initialization.
SimpleService.groovy
import javax.annotation.PostConstruct
class SimlpeService {
private SimpleClient simpleClient
def grailsApplication
#PostConstruct
void postConstruct() {
def config = grailsApplication.config.client.data
SimpleClient simpleClient = SimpleClientBuilder
.withURL(config.url)
.withAuth('luggageCombo', config.username)
.isOptimized(config.optimized)
.build()
}
Buzz doSomething(String derp) {
// ...
Fizz fizz = simpleClient.doSomething(derp)
// ...
fizz.asBuzz()
}
}
So, Grails or Spring will call this method postConstruct() automatically when all the dependencies (in this case grailsApplication) for this service are resolved and any of the service method is invoked.
This has been taken care that that method must invoke before you access any field member or method of the SimpleService.
Now, everything is already configured like you mentioned that you may need to call different URL with different credential, just you have to define them in your Config.groovy as:
environments {
development {
client {
data {
url = "simpleclientdev.example.com"
username = "test"
optimized = false
}
}
}
production {
client {
data {
url = "simpleclient.example.com"
username = "johndoe"
optimized = true
}
}
}
}
Now when you do run-app with development mode and calling simpleService.doSomething() in your example controller will automatically hit the simpleclientdev.example.com URL with test credential and when you deploy it using production environment, the same simpleService.doSomething() method will hit simpleclient.example.com with optimized set to true.
Update
The key point here based on your question is that we will not be injecting different instances of SimpleService since services are singleton by default. Instead we are changing the value's associated with the service based on the environment.
Sounds like you need to understand a bit more about how to leverage Spring maybe?
Grails Spring Docs
You can also do things like so in your Service class
#PostConstruct
void init() {
//configure variables, etc here ...
log.debug("Initialised some Service...")
}
I want to call a service inside my grails domain objects beforeDelete() event. Unfortunately it always crashes reproducibly when the event is fired. I built an example that reproduces the problem. The domain object:
class Gallery {
def myService
def beforeDelete() {
// System.out.println(myService); // not even this works, same error!
System.out.println(myService.say());
}
}
The service:
class MyService {
String say() {
"hello"
}
}
The test controller:
class DeleteController {
def index() {
Gallery.list().each {
it.delete()
}
}
def create() {
def gallery = new Gallery()
gallery.save()
}
}
If I start the application and call create followed by index I get:
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [testbeforedelete.Gallery#1]
What I want to accomplish is to call my service, which is a bit more complicated than this example. I cannot explain this behavior and I don't know how cope with this. I know that the Hibernate events need special care yet I'm stuck.
The beforeDelete actually makes a change to your domainclass. I agree that you would not expect this behaviour. Hibernate thinks that you are modifying the instance. You could use the follow code to get around your problem
def beforeDelete() {
Gallery.withNewSession {
System.out.println(myService.say());
}
}
I have some (non-Grails-artifact) classes that access the service layer beans via passing around the grailsApplication object. However I'm having trouble unit testing the classes implemented in this way. Why doesn't the bean get registered in the main context?
#TestMixin(GrailsUnitTestMixin)
class ExampleTests {
void setUp() {}
void tearDown() {}
void testSomething() {
defineBeans {
myService(MyService)
}
assert grailsApplication.mainContext.getBean("myService") != null
}
}
The above code fails with:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'myService' is defined
What I'm trying to do is access services from plain old Java classes via the grailsApplication. This works, but not in unit test environment. Should I do it differently?
class POJO {
MyService myService;
public POJO(GrailsApplication grailsApplication) {
myService = (MyService) grailsApplication.getMainContext().getBean("myService");
}
}
The answer is that in the GrailsUnitTestMixin the applicationContext that holds your beans is set as the parentContext in the grailsApplication
beans.registerBeans(applicationContext)
static void initGrailsApplication() {
...
//the setApplicationContext in DefaultGrailsApplication set's the parentContext
grailsApplication.applicationContext = applicationContext
}
So you can get your beans with:
defineBeans {
myService(MyService)
}
assert applicationContext.getBean("myService")
assert grailsApplication.parentContext.getBean("myService")
EDIT
Today I faced the same problem, and my solution is:
#Before
void setup() {
Holders.grailsApplication.mainContext.registerMockBean("myService", new MyService())
}
In my case (grails 2.4.4) the accepted solution didn't work but pointed me in the right direction, this line worked instead as the bean factory in the mainContext within my unit test was an OptimizedAutowireCapableBeanFactory
Holders.grailsApplication.mainContext.beanFactory.registerSingleton('myBean', new MyBeanClass())
I have spent some time with the same issue, in my case running grails 2.2.4 and having (in src/groovy):
import grails.util.Holders
class SomeClass {
transient myService = Holders.grailsApplication.mainContext.getBean 'myService'
.....
}
Which is a bit different to question author, but at least it will be useful for someone coming from search engine results
Nevertheless accepted answer did not work for me, so I came up with a bit different approach of mocking and registering service used in SomeClass.
import grails.util.Holders
.. other imports
#TestMixin(GrailsUnitTestMixin)
class SomeClassTests {
#Before
void setUp() {
Holders.grailsApplication = grailsApplication
defineBeans {
myService(MyServiceMock)
}
}
....
}
class MyServiceMock extends MyService {
// overriden methods here
}
I'm writing a grails plugin and I need to hook into the domain save() method to do some logic after the save. I need to do this across multiple domain classes. I'm trying to avoid hibernate events in the cases where a plugin user is not using hibernate with GORM.
I've tried many thing but below is what I think should have had the best chance at working. In all cases grailsSave is null. How can I do this?
def doWithDynamicMethods = { ctx ->
application.domainClasses.each { dc ->
def grailsSave = dc.metaClass.pickMethod('save', [Map] as Class[])
domainClass.metaClass.save = { Map params ->
grailsSave.invoke(delegate, [params] as Object[])
println "Saved object, now do my thing"
//...
}
}
}
I have the following set in my *Plugin.groovy class:
def dependsOn = [domainClass: '1.1 > *', hibernate: '1.1 > *']
def loadAfter = ['hibernate']
I was unable to successfully get a reference to the save() methods during plugin/app initialization; I don't know why. Instead, I decided to create a listener for the hibernate events after insert, update, and deletes. This post by Sean Hartsock regarding the Audit Logging plugin was a perfect primer for doing that.
Here's the gist of the Listener:
class MyListener implements PostInsertEventListener, PostUpdateEventListener, PostDeleteEventListener, Initializable {
public void onPostInsert(final PostInsertEvent event) {
// logic after insert
return
}
public void onPostUpdate(final PostUpdateEvent event) {
// logic after update
return
}
public void onPostDelete(final PostDeleteEvent event) {
// logic after delete
return
}
public void initialize(final Configuration config) {
return
}
}
Then in the *GrailsPlugin.groovy:
def doWithApplicationContext = { applicationContext ->
// add the event listeners for reindexing on change
def listeners = applicationContext.sessionFactory.eventListeners
def listener = new MyListener()
['postInsert', 'postUpdate', 'postDelete'].each({
addEventTypeListener(listeners, listener, it)
})
}
// copied from http://hartsock.blogspot.com/2008/04/inside-hibernate-events-and-audit.html
private addEventTypeListener(listeners, listener, type) {
def typeProperty = "${type}EventListeners"
def typeListeners = listeners."${typeProperty}"
def expandedTypeListeners = new Object[typeListeners.length + 1]
System.arraycopy(typeListeners, 0, expandedTypeListeners, 0, typeListeners.length)
expandedTypeListeners[-1] = listener
listeners."${typeProperty}" = expandedTypeListeners
}
Fairly simple at the end of the day...
There are three different version of save added to the metaClass,
save(Map)
save(Boolean)
save()
Which one are you calling in your testing? You'll need to add you code to each one.
Another thing to check is whether your plugin is running after the hibernate plugin which adds the three methods to the metaClass
cheers
Lee
Have a look at the Falcone Util plugin. This plugin allows you to hook into Hibernate events (see documentation at the bottom of the page). I don't know if this is exactly what you want, but you might get some hints.
Ps! I don't think the plugin works with Grails 1.2 yet.
This is an issue of premature optimization: older versions of Groovy seriously penalized MetaClass mangling, and so GORM does not add all of its magic until it detects the need to.
Easiest solution is to have your plugin dependOn GORM Labs (I work around it there). The alternative solution is to trigger methodMissing manually (which would be duplicating the work I did). See the GORM Labs documentation for details on how I accomplished that.
Wouldn't this best be added to the service class that owns the unit of work? That's where the usual Spring/Grails idiom would have such logic. You needn't modify the save at all.
Additional GORM methods are lazily initialized on first call to any of them.
To initialize them in doWithDynamicMethods simply call one of the static methods on your domain class(es):
def doWithDynamicMethods = { ctx ->
application.domainClasses.each { dc ->
// call any static method to initialize dynamic gorm methods
dc.clazz.count()
def grailsSave = dc.metaClass.pickMethod('save', [Map] as Class[])
//...
}
}
Your save() method will be available now. As this is called at start up a single count shouldn't be to much of a problem.