I want to use a service in my Grails application. However, it is always null. I am using Grails version 1.1. How can I solve this problem?
Sample code:
class A{
String name;
def testService;
static transients=['testService']
}
Can I use a service inside a domain class?
That should work. 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.
The typical use case for injecting a service into a domain class is for validation. A custom validator gets passed the domain class instance being validated, so you can access the service from that:
static constraints = {
name validator: { value, obj ->
if (obj.testService.someMethod(value)) {
...
}
}
}
Short answer is. Yes you can use service inside domain class.
Here is an sample code where domain class gets access to the authenticate service from acegi plugin. It works without problems.
class Deal {
def authenticateService
def afterInsert() {
def user = authenticateService.userDomain();
....
}
....
}
To summarize Burt and Remis' answers:
In the domain custom validator, you have to use obj.testService rather than use testService directly. If you wanna use service in the domain custom validator:
static constraints = {
name validator: { value, obj ->
if (obj.testService.someMethod(value)) {
...
}
}
}
But in other methods, including afterInsert and other private/public methods, use testService is fine.
def someMethod() {
def user = testService.serviceMethod();
....
}
Related
I wish to make DAO layer in my grails project which would be not be associated with any of the domain classes and would be interacting with the secondary database of my project. I get the following error when I try to inject the service in any controller:
"Cannot invoke method abc() on null object"
However, the error is resolved and works perfectly when I initialise the service using the new keyword in the controller but I know that shouldn't be necessary as grails is supposed to handle it. Can anyone tell me what am I missing?
I don't think the issue has anything to do with whether or not the service is associated with a domain class. The DI container doesn't know anything about that.
If you have a controller like this:
// grails-app/controllers/demo/SomeController.groovy
package demo
class SomeController {
SomeService someService
def someControllerAction() {
someService.abc()
// ...
}
}
And a service like this...
// grails-app/services/demo/SomeService.groovy
package demo
class SomeService {
void abc() {
// ...
}
}
That will work fine.
It is almost impossible to say for sure without seeing what your code what you are doing wrong but one possibility is something like this, which will not work:
// grails-app/controllers/demo/SomeController.groovy
package demo
class SomeController {
def someControllerAction() {
// This is a local variable, not
// a property and as such will not
// be subjected to dependency injection.
SomeService someService
// ...
someService.abc()
// ...
}
}
Also, make sure the property name (someService in the sample above) matches the service class name, but with a lower case first letter (more generally, make sure the property name matches the property name representation of the service class name, which is usually as simple as lower casing the first letter of the class name).
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
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.
I would like to make use of the g.message() functionality in the toString method of my domain class, but the g.-namespace is not accessible by default.
I doubt that a import g.* will do the trick.
I already know that I can use the messageSource functionality, but it would be nicer to use the same syntax as in the views.
You can use:
class MyDomain {
def someMethod() {
def g = ApplicationHolder.application.mainContext.getBean( 'org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib' )
return g.message(....)
}
}
or else you can get messageSource directly: ApplicationHolder.application.mainContext.getBean('messageSource')
Using g.render in a grails service has some hints how to use "g:" in a service. I have not tested this, but it should work mostly the same in domain classes, with one important exception: a domain class cannot use InitializingBean since it's not a bean residing in the application context.
Is it possible to metaprogram named queries onto a grails domain class? If so, how?
Thanks
Domain classes have a namedQueries property that you can use to add your own named queries. If you want to do this using metaprogramming from within a plugin (rather than by editing the domain class directly), you should do it in the doWithDynamicMethods closure of the plugin's descriptor file.
Something like this should work:
class MyPlugin {
def doWithDynamicMethods = { applicationContext ->
application.domainClasses.each { domainClass ->
boolean domainClassFilter = domainClass as Boolean
if (domainClassFilter) {
domainClass.metaClass.static.myNamedQuery = {->
// implementation of your named query goes here. Here is an example implementation
// that returns all instances with status == 'ready'
String simpleClassName = domainClass.simpleName
domainClass.findAll("from $simpleClassName where status = ?", ['ready'])
}
}
}
}
}
This will add myNamedQuery to every domain class in the application that the plugin is installed in. If you only want to add it to some domain classes, then replace the value of domainClassFilter with a more appropriate test.