I want to implement the concept of Trigger objects in Grails for my domain classes. Grails allows me to define methods like beforeInsert() in my domain classes to handle insert, update and delete events, similar to how SQL databases have the concept of database triggers. But to separate concerns I'd like to move all of the trigger logic outside of the domain class.
Ideally I can simply define a static list of trigger classes in my domain class and then iterate through those in the beforeInsert() method. Sample code shown below.
static beforeInsertTriggers = [AccountNameTrigger, AccountDumpTrigger]
def beforeInsert() {
for (Class<Trigger> triggerClass : beforeInsertTriggers) {
triggerClass.newInstance().using(this).execute()
}
}
I've created a sample Grails 2.5.4 project on GitHub illustrating what I'm trying to do. But the problem is that the triggers literally are standalone islands of logic without the ability to autowire services. How can I better set up this Trigger pattern or something similar? So I may autowire services and other beans into the Trigger instances?
I am also trying to keep the design simple and readable.
Avoid cluttering the domain class with fields and properties that are not related to the data model. To me this means no autowired beans in the domain class.
Define the list of triggers using the trigger class names (or bean names, if need be). This may be overly idealistic, but hey...
For what it's worth, part of my inspiration also comes from how Salesforce implements triggers as distinct units of self-contained code.
Register your Triggers as Singleton Beans and there you can inject other services/beans. You can create your custom beans via resources.groovy.
Lets take the example of AccountDumpTrigger. Lets make a simple change to it:
package grails.domain.trigger.demo.triggers
import grails.domain.trigger.demo.Account
import org.codehaus.groovy.grails.commons.GrailsApplication
/**
* Created by marty on 6/25/16.
*/
class AccountDumpTrigger extends AbstractTrigger<Account> {
GrailsApplication grailsApplication
#Override
void execute() {
println grailsApplication.isInitialised()
println resource.dump()
}
}
And code in resources.groovy or in your plugin's doWithSpring closure:
accountDumpTrigger(grails.domain.trigger.demo.triggers.AccountDumpTrigger) { bean ->
bean.factoryMethod = 'getInstance'
/*
either refer each bean individually or you can use:
bean.autowire = "byType"
OR
bean.autowire = "byName"
*/
grailsApplication = ref("grailsApplication")
}
And inside your domain:
static beforeInsertTriggers = [AccountDumpTrigger]
def beforeInsert() {
for (Class<Trigger> triggerClass : beforeInsertTriggers) {
triggerClass.instance.using(this).execute()
}
}
And instead of writing your code inside beforeInsert you can also do the same by registering an implementation of AbstractPersistenceEventListener. This way you don't have to repeat your code. Also yo can move it to a parent class.
Related
I have the following super class grails service:
abstract class SuperClassService {
public def execute(def payload) {
def tracker = new TrackerDomain().save()
doWork()
tracker.status = 'done'
tracker.save()
}
protected abstract doWork(def payload);
}
and seveeral child class grails services that follow this pattern:
class SubClassService extends SuperClassService {
protected doWork(def payload){
new SomeDomain().save()
}
}
In my controllers I kick off a call to the 'execute' method of the various child classes.
What I want is for the SubClass services to follow the traditional Service pattern where any problems get rolled back, but I want the domains created int he parent class code to both NOT be rolled back and be committed immediately (so that they can be viewed on a tracking page while the subclass service code is still executing. I would PREFER not to set everything as non-transactional and only set the functions in the subclass as transactional but if that's the only option here I would like to know that too.
Have you tried annotating your subclasses service method with a #Transactional(propagation = Propagation.REQUIRES_NEW)? I think that it should do the trick, regardless of whether the outside service code is transactional or not.
Apologies, since there are lots of links out there and I have tried a few examples including stuff that already works within my project(s) that are non plugin:
What I have tried:
Within my Plugin descriptor:
def doWithSpring = {
someService(SomeService)
}
Then within my end src/groovy
//def someService = Holders.grailsApplication.mainContext.getBean 'someService'
def someService
None of above works...
If I instantiate the service everything appears to work fine, I would prefer to inject it and its just taking a lot of time doing something rather basic :(
SomeService someService=new SomeService()
Any help would be appreciated
Not that I have ever seen it before (within a plugin) should I include conf/spring/resources.groovy and initialise bean in there ?
In this case, like most cases, there's a way to access what you want without using holders. The Groovy class implements ServletContextListener, so there's a contextInitialized method with a ServletContextEvent event containing the ServletContext. So it wasn't necessary to use the ServletContextHolder to get the ServletContext - it was right there. You can see from the FAQ that the Spring ApplicationContext is stored in the ServletContext as an attribute. Once you have that, you can access whatever Spring beans you want; in this case the jenkinsService and the grailsApplication beans (you can get the config directly from the grailsApplication without using Holders for that.
I made these changes and did a bunch of cleanup, and sent a pull request.
You can inject your service in a src/groovy class like so:
import com.example.SomeService
import grails.util.Holders
class SrcGroovy {
SomeService someService = Holders.applicationContext.getBean("someService")
// ...
}
I've never used Grails services before, but according to my Web searches, they can be injected into domain objects. So, if I have the following domain class (assume that BookService is defined somewhere),
class Book {
BookService bookService
}
when I instantiate a Book object using, def book = new Book(), book.bookService should be set?
If that's the case, what if I want to inject external services (or service-like components)? For example, if there's an ISBN API client available, can I expect grails to inject it the same way? For example, say the client is IsbnApi, how do I automatically inject like it's another service? Will the code below work?
class Book {
#Transient
#Autowire
IsbnApi isbnApi
}
Based on the earlier responses to this question, I've already defined the domain class this way:
class Book {
static transients = ['isbnApi']
IsbnApi isbnApi
}
Now, this may be all that I need, but I also want to test that the automatic injection would work even without having to run the app. In my unit tests, I have to set the mock "service" manually.
IsbnApi isbnApi = Mock()
Book book = new Book()
book.isbnApi = mockIsbnApi
I attempted to check automatic injection by moving the test to test/integration and set the beans using doWithSpring.
def doWithSpring() {
isbnApi = (InstanceFactoryBean, mockIsbnApi, isbnApi)
}
void "test automatic injection"() {
given:
IsbnApi isbnApi = Mock()
Book book = new Book()
//removed: book.isbnApi = mockIsbnApi
expect:
book.isbnApi //this fails
}
I also found additional information from this answer to a similar question. In particular,
Note that since you're using 'def' you don't need to add it to the
transients list. Are you trying to access it from a static method?
It's an instance field, so you can only access it from instances.
By convention Grails services are automatically candidates for dependency injection (= they can be injected into other classes). If you want to other classes you have to add them to conf/spring/resources.groovy.
E.g.:
import foo.bar.IsbnApi
beans = {
isbnApi(IsbnApi)
}
Have a look at the Bean Builder documentation section.
To make sure that your field is not treat as a persistent property, you can use the static transients field.
For example:
class User {
def grailsApplication
static transients = ['grailsApplication']
}
See transients documentation
In addition to the already accepted answers, as from grails 3.2.x you will need to enable autowiring on the domain ojbect in the mapping block or globally in the application config
class Book {
static transients = ['isbnApi']
IsbnApi isbnApi
static mapping = {
autowire true
}
}
It is disabled by default to improve read performance so perhaps it's best to find alternative approach instead of enabling it.
http://grailsblog.objectcomputing.com/posts/2017/05/09/injecting-a-service-into-a-domain-class.html
So I created a plugin that introduces a custom artefact "Guard" (guard classes resides in: /grails-app/guards). Right now my plugin works by making the grailsApplication aware of the new artefact. So if I call grailsApplication.getGuardClasses() the behavior performs as expected.
My problem is that I want to be able to dynamically inject services into my guard classes, like so:
Class ExampleGuard {
def exampleService
}
I have found solutions on this website that give a static solution, that either goes of Holders, or the application context to get the bean by name. This is not what I want because I essentially want this to be autowired up to that when people use my plugin they can use dynamic injection of services in the guard classes.
UPDATE:
This is how I was accessing my Guard class.
def guard = grailsApplication.getArtefact("Guard","marketplace.TestGuard").newInstance()
Calling .newInstance() nullifies all fields on the class including the injected spring beans which is why injected classes will be null:
assert guard.sampleService == null
If your plugin discovers all of the Guard classes and adds them to the spring application context, those beans will automatically be subject to dependency injection. This is how all of the built in artifacts work and how artifacts provided by plugins work, Quartz Jobs for example.
EDIT:
As an exmaple...
class GuardsPlugin {
// ...
def doWithSpring = {
application.guardClasses.each { guardClass ->
"${guardClass.propertyName}"(guardClass.clazz) { bean ->
bean.autowire = "byName"
}
}
}
}
Then if you have a class like MySpecialGuard and you reference if from a controller (for example) with something like this...
class MyController {
def mySpecialGuard
def someAction() {
// mySpecialGuard should be DI'd into this
// controller and mySpecialGuard should itself
// have been subjected to DI
}
}
I hope that helps.
I have a requirement, my current project is with ejb3, jpa and jsf. I have a core development project and customer specific project. In the customer specific project, we planned to inherit the core classes and extend or override the core functionality. So we planned to create the customer specific classes with some customer prefix. So in the final war file, we will have the core class and all customer prefix classes. For example, if Authenticator is a core class and XXAuthenticator, YYAuthenticator are customer specific classes exist in the build. So, for example if i have a line of code in a bean like this:
#Inject Authenticator authenticator;
Can i programmatically and/or dynamically inject the inherited classes based on some logic like logged in user has a customer specific functionality.
what i am expecting is, i dont want to change the above line to inject, because it will be big change in every class. But expecting some kind of dynamic logic or configuration file to change the core class injection to customer specific class..
So finally with out touching the #Inject Authenticator authenticator; line. Can I inject the xxAuthenticator or YYAuthenticator through some logic? We dont have Spring in project technology stack.. So please suggest me with out spring only.. Thanks in advance
It sounds like your use case is more around Qualifiers. If a user follows a certain, you should be injecting different qualified versions of classes to use, no?
#Inject
private Instance<SomeService> someServiceInstance;
// later on...
SomeService someService = null;
if(someCondition) {
someService = someServiceInstance.select(new FooLiteral()).get();
}
else {
someService = someServiceInstance.select(new BarLiteral()).get();
}
Where FooLiteral and BarLiteral are annotation literals for #Foo and #Bar, which are qualifiers.
In CDI this is done with "producer methods".
It would look like this:
#ApplicationScoped
public class AuthenticatorProducer {
private Authenticator xxAuthenticator; // = ...
private Authenticator yyAuthenticator; // = ...
#Produces
public Authenticator getAuthenticator() {
if (someCondition) {
return xxAuthenticator;
} else {
return yyAuthenticator;
}
}
}
No need to change injection points and Authenticators don't have to be CDI beans themselves.