I have a validateable object. When I instantiate it with new from an input Map, the ApplicationContext that should be embedded in the command object is not populated, and the invocaction fails when I call validate(), with the following Exception:
java.lang.IllegalStateException: Could not find ApplicationContext, configure Grails correctly first
What do I have to do to get a proper spring bean when I want to create a Validateable object and bind it to a Map of values?
Please note This code does not run in a grails controller: it runs in a rabbitMq consumer. So simple solutions applying to controllers will not work here.
class MyConsumer {
static rabbitConfig = [
queue : 'myQueue'
]
def handleMessage(Map message, MessageContext context) {
def request = new MyValidateableRequest(message) //instantiates just a POGO
if (request.validate()) { //Exception here
//...
} else {
//...
}
}
}
It depends on Grails version and if you use plugin for RabitMQ. Normally consumer should be a spring bean and you can use code like below. grailsApplication is bean which should be injected into consumer
def object = new MyValidateableRequest()
//do request binding here
object.properties = [message: message]
AutowireCapableBeanFactory acbf = grailsApplication.mainContext.autowireCapableBeanFactory
acbf.autowireBeanProperties object, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false
object.validate()
If you are on Grails 3, example which will not require plugin
https://spring.io/guides/gs/messaging-rabbitmq/ may be more interesting
Related
Grails services are abstractions used for implementing business logic (as well as connecting to backing services/DBs, etc.) outside of a controller. So in a typical controller you might have:
class DashboardController {
StatisticsService statsService
def index() {
// Fetches all the stats that need to be displayed to the
// admin on the dashboard.
AdminDashboardMetrics adm = statsService.getAdminStats()
render(view: "/dashboard", model: [ adm: adm ])
}
}
Here, Grails automatically injects the DashboardController with a bean instance of StatisticsService (provided of course that the service was properly created with grails create-service ...).
But what happens when I need to access StatisticsService outside of a controller, and particularly, under src/groovy?
// src/groovy/com/example/me/myapp/FizzBuzzer.groovy
class FizzBuzzer {
StatisticsService statsService
FizzBuzzer(StatisticsService statsService) {
super()
this.statsService = statsService
}
def doSomething(MyData input) {
MoreData result = statsService.calculate(input)
// use 'result' somehow, etc....
}
}
How do I properly inject FizzBuzzer with the same StatisticsService instance ad what is passed into DashboardController?
You can inject grails service in spring bean by defining injection login in resources.groovy under conf > spring
As I made an ExampleService and Example class in src/groovy
ExampleService
class ExampleService {
def serviceMethod() {
println "do something"
}
}
Example Class under src/groovy
class Example {
ExampleService exampleService
def doSomething() {
def result = exampleService.serviceMethod()
}
}
resources.groovy under conf>spring
beans = {
ex(Example){ bean ->
exampleService = ref('exampleService')
}
}
so I can define Example ex as spring bean in grails-app and it will have ExampleService injected by itself.
Hope this helps. Thanks
You can also get a service using grails.util.Holders
Example:
For injecting MyService service class, use Holders.applicationContext.getBean("myService")
Where "myService" is the name of your service class in lower camel case.
One way of achieving this is by using ServletContext-
ApplicationContext ctx = (ApplicationContext)ServletContextHolder.
getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
statisticsService = (StatisticsService ) ctx.getBean("statisticsService ")
see this blog - http://www.grailsbrains.com/availing-grails-goodies-in-srcjava-or-srcgroovy/
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 have a class that I moved under the grails-app/services directory in order to inject the springSecurityService. This class is the implementation (Is that the proper terminology?) of the spring userDetailsService class. Here is my declaration in resources.groovy:
userDetailsService(com.company.product.PracticeUserDetailsService) {
grailsApplication = ref('grailsApplication')
}
This class is extending GormUserDetailsService.
My attempted dependencyInjection results in a null object.
class PracticeUserDetailsService extends GormUserDetailsService{
def springSecurityService
UserDetails loadUserByUsername(String username, boolean loadRoles) throws UsernameNotFoundException {
// impl omitted
}
}
If I create some test controller or some service in grails and inject the springSecurityService, it works fine. So, there is perhaps something about this particular class that is not placing it in the Grails ecosystem. I checked out this to try to manually inject it as follows:
beans = {
springSecurityTEST(grails.plugins.springsecurity.SpringSecurityService) {}
}
Move PracticeUserDetailsService from grails-app/services to src/groovy and change your Spring bean definition in resources.groovy to:
userDetailsService(com.company.product.PracticeUserDetailsService) {
grailsApplication = ref('grailsApplication')
springSecurityService = ref('springSecurityService')
}
For me it happened when I used GrailsApp.run(Application, args) to run the app...
Im having some problems trying to actually determine if my beans have been properly loaded.
Is there some log4j property which can show me in the log what beans that are properly loaded?.
After some trying i went off and tried another example from here
redefining my resources.groovy like follows:
import grails.spring.BeanBuilder
BeanBuilder bb = new BeanBuilder()
bb.beans = {
ldapUserDetailsMapper(example.CustomDetailsContextMapper) {
}
}
CustomDetailsContextMapper in its turn is defined as:
class CustomDetailsContextMapper implements UserDetailsContextMapper {
def springSecuritySource
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection authorities) {
This is the error im getting when using this kind of resource.groovy setup:
2012-10-12 12:52:31,663 [main] ERROR spring.GrailsRuntimeConfigurator - [RuntimeConfiguration] Unable to load beans from resources.groovy
org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack: No such property: beans for class: resources
Possible solutions: class
When not using beanbuilder i get no errors but i cant really verify that my beans are loaded, since they seem never to be called.
In the other case i get the below error.
Any suggestions?
in resource.groovy simply write
beans = {
ldapUserDetailsMapper(example.CustomDetailsContextMapper) {
}
The BeanBuilder is already defined for you
A bean is called when used... inside a controller try thi
VeryBeanController{
def ldapUserDetailsMapper
def index = {
render( ldapUserDetailsMapper )
}
}
And call the controller
I have a service to get and set the user in the session. I would like to pass in some user information to each view if there is a logged in user and thought a filter would be the best way so I don't have to duplicate that in every controller/action. When I run the app, it get this error:
Error creating bean with name 'userService': Scope 'session' is not active for the current thread
My filter looks like this:
class SecurityFilters {
def userService
def filters = {
setUser(controller:'*', action:'*') {
before = {
if (userService.isLoggedIn()) {
request.user = userService.getUser()
} else {
request.user = null
}
}
}
}
}
I know I can just ultimately access the user through session.user, but I want to be able to call userService.isLoggedIn() which I can't easily through the view. So is there a way to inject the service into the filter, or should I just create a taglib to wrap userService.isLoggedIn()?
It looks like the problem is your userService is scoped to the session, and there is not necessarily a session at the time the attempt to inject the service into the filter happens.
If your userService is necessarily session-scoped then you need to use a scoped proxy in your spring config. E.g. in grails-app/conf/spring/resources.groovy:
import com.your.service.UserService
...
userServiceSession(UserService)
{ bean ->
bean.scope = 'session'
}
userServiceSessionProxy(org.springframework.aop.scope.ScopedProxyFactoryBean)
{
targetBeanName = 'userServiceSession'
proxyTargetClass = true
}
Then rename your injected variable in your SecurityFilter:
def userServiceSessionProxy
(and obviously rename where you use it elsewhere in the class).
What this should do is inject the proxy at injection time, but only go to the actual service at the time the filter is executed (when there is a session).
Note: not sure whether doing this still lets other places where there will be a session (e.g. controllers) still reference the service as 'userService', if not you might be able to rename userServiceSession to userService in resources.groovy (and update targetBeanName accordingly).
It looks like there are some problems with services in filters. This bug might point you in the right direction: https://jira.grails.org/browse/GRAILS-5982
You can also try to get a reference to your service through the ApplicationContext. Here's an example: How do I get an instance of a Grails service programmatically?
import org.codehaus.groovy.grails.web.context.ServletContextHolder
import org.springframework.context.ApplicationContext
import org.springframework.web.context.support.WebApplicationContextUtils
class SecurityFilters {
def userService
def filters = {
setUser(controller:'*', action:'*') {
before = {
def servletCtx = ServletContextHolder.getServletContext()
ApplicationContext applicationContext = WebApplicationContextUtils.
getRequiredWebApplicationContext(servletCtx)
userService =applicationContext.getBean('userService')
if (userService.isLoggedIn()) {
request.user = userService.getUser()
} else {
request.user = null
}
}
}
}
}