I have a Grails webapp running a Spring Integration inbound-channel-adapter that is configured to receive emails and there is a service activator that processes the message. This service activator pulls out pieces and parts of the email based on business rules and then needs to update Grails domain objects and save those changes to the database.
Spring Integration service activator code snippet:
HashMap<String, Serializable> params = new HashMap<String, Serializable>();
params.put("notification", notification );
params.put("notificationType", notificationType );
params.put("sender", notificationSender );
InvokerHelper.invokeMethod(NotificationCreationService.class, "createNotification", params);
Grails NotificationCreationService action snippet:
def static createNotification() {
if (params.notification != null && params.notificationType != null && params.sender != null) {
String notification = params.get("notification") as String
NotificationType notificationType = params.get("notificationType") as NotificationType
String sender = params.get("sender") as String
def NotificationMessage notificationMessage = null
notificationMessage = new NotificationMessage()
notificationMessage.notification = notification
notificationMessage.save(flush: true)
...
}
}
NotificationMessage is a standard Grails domain class
Error generated:
org.springframework.integration.MessageHandlingException:
groovy.lang.MissingMethodException: No signature of method: mycompany.pyproject.mypackage.NotificationMessage.save() is applicable for argument types: () values: []
Possible solutions: save(), save(boolean), save(java.util.Map), wait(), any(), wait(long)
If I change createNotification() to not be static then I get the following:
org.springframework.integration.MessageHandlingException:
groovy.lang.MissingMethodException: No signature of method: static mycompany.pyproject.mypackage.NotificationCreationService.createNotification() is applicable for argument types: (java.util.HashMap) values: [...]
Possible solutions: createNotification(java.util.HashMap), createNotification(java.lang.String, napa.changedetection.alert.NotificationType, java.lang.String)
I've tried other combinations of InvokeHelper.invokeMethod() and the definitions of the Grails actions with similar results. InvokeHelper has several other methods for invoking methods as well as setting different properties, but I haven't found the magic combination to make this work and any internet searched have turned up very little as far as example code for InvokeHelper.
Ideally, the Grails action would not be static and it would use the standard params mechanism. This would allow me to call the same action directly from the Grails code as well as from the Spring Integration service activator via the InvokeMethod functionality.
Does anyone have any thought on how to tie this together?
You should not use InvokeHelper now, Grails 2 injects signature at compile time for java purposes (except dynamic finders, but you can guess why).
In your Java bean I would inject notificationCreationService using :
resources.groovy/xml + setter
OR
Constructor + applicationContext (implement ApplicationContextAware)
Using the service reference should work, if not there is a bug to analyse there.
Where did you declare your service activator ?
Related
i'm new to Grails i'm using Grails 2.5.1 , i'm creating a service but when i call this service i get the bellow errors :
Class:groovy.lang.MissingPropertyExceptionMessage:No such property: flash for class: com.sherif.UtilsService Possible solutions: class
and
Class:groovy.lang.MissingMethodExceptionMessage:No signature of method: com.sherif.UtilsService.render() is applicable for argument types: (java.util.LinkedHashMap) values: [[view:forget]] Possible solutions: every(), find(), find(groovy.lang.Closure), every(groovy.lang.Closure)
here is my service :
class UtilsService {
def sendPassword(params) {
def enteredCeredintials = User.findByEmail(params?.email)
if (enteredCeredintials?.email==null) {
flash.message = message(code: 'user.email.notfound', args: [params?.email])
}
else {
flash.message = message(code: 'user.passwordSent', args: [params?.email])
}
render (view: "forget")
}
}
what may caused these errors ?
Thanks
flash is temporary storage used in the web tier - it's one of the implicit variables that's available in controllers, along with request, response, session, etc. But it's not available in services - there are no implicit variables in services.
Ideally you should keep each tier separate. Services shouldn't work with or be aware of the web tier - they're helper classes that use business logic to perform various tasks, work with the database, etc. They're called by controllers, but shouldn't be polluted with web tier concepts.
To fix this, extract out the logic that is independent of the web tier and do that work in the service. Then return whatever data you need to render the view in the controller, and in this case to additionally store some data in flash scope.
I registered my services.yml file like below :
services:
PMI.form.users_tasks:
class: PMI\UserBundle\Form\UsersTasksType
arguments:
EntityManager: "#doctrine.orm.default_entity_manager"
I can list it by php app/console container:debug, so that mean my service is registered properly.
In my UsersTasksType class I have like below :
class UsersTasksType extends AbstractType
{
protected $ur;
public function __construct(EntityManager $ur )
{
$this->setUr($ur);
}
// Get and setters
}
Does Dependency Injection mean that I don't have to pass the EntityManager to the class constructor anymore? Or what ?
Because when I have to run the code below :
$form = $this->createForm(new UsersTasksType(), $entity);
I get this error:
Catchable Fatal Error: Argument 1 passed to PMI\UserBundle\Form\UsersTasksType::__construct() must be an instance of Doctrine\ORM\EntityManager, none given, called in C:\wamp\www\PMI_sf2\src\PMI\UserBundle\Controller\UsersTasksController.php on line 74 and defined in C:\wamp\www\PMI_sf2\src\PMI\UserBundle\Form\UsersTasksType.php line 19
And I have to do something below :
$em = $this->container->get('doctrine.orm.entity_manager');
$form = $this->createForm(new UsersTasksType($em), $entity);
So what would be the whole purpose of Dependency Injection ?
Dependency Injection basically gives one service (in this case, your UserTasksType) access to another service (in this case, your the entity manager).
arguments:
EntityManager: "#doctrine.orm.default_entity_manager"
These two lines tell Symfony to expect the entity manager service to be passed into the constructor when you instantiate a new UserTasksType object, which effectively gives your UserTasksType access to the entity manager.
If you aren't using the entity manager in your UserTasksType, there is no need to inject it in the constructor and you could get rid of the two lines above and the __construct() / setUr() methods in your UserTasksType.
A better example to help you understand DIC might be that you have a service that is written specifically to send emails (Swiftmail, for e.g.) and you need to inject it into another service so that service can send emails.
By adding
arguments: [ #mailer ]
to your service definition, your services constructor will expect your mailer service
__construct ($mailer)
{
$this->mailer = $mailer;
}
which will give it access to send emails
someFunction()
{
//do something useful, then send an email using the swift mailer service
$this->mailer->sendEmail();
}
Check out the latest Symfony docs for more of an explanation.
http://symfony.com/doc/current/book/service_container.html
I am trying to use dependency injection for export to excel functionality provided by "Export Plugin"
It seems that whenevery I try to use service in my project i get error like following
Stacktrace follows:
java.lang.NullPointerException: Cannot invoke method export() on null object
at pm.ProjectsController$_closure2.doCall(ProjectsController.groovy:39)
at pm.ProjectsController$_closure2.doCall(ProjectsController.groovy)
at java.lang.Thread.run(Thread.java:662)
The code I am using is following, this just means service variable is null
def exportService// i have tried with and without the initialization
if(params?.format && params.format != "html"){
response.contentType = ConfigurationHolder.config.grails.mime.types[params.format]
response.setHeader("Content-disposition", "attachment; filename=books.${params.extension}")
exportService.export(params.format, response.outputStream,projectsList, [:], [:])
}
It seems that no plugin that uses services is working in my project for example AsynchronousMailService in my project didn't work as it was suppose to and thus I have been using it like following
AsynchronousMailService asynchronousMailService = new AsynchronousMailService()
asynchronousMailService.sendAsynchronousMail {
to projectsInstance.projectLead.emailAddress
subject "New project is assigned to you"
html msg
}
Unless I am missing somethig very basic I do not beleive I should be instantiating this class if the plugin offers the same as service.
Thanks
Right, you should never instantiate services or other Spring beans - use dependency injection. It might work, but if the bean has any dependencies of its own they'll be null since you're bypassing Spring.
You're not using dependency injection, you're declaring a local variable and expecting magic.
Dependency injection in Grails uses public fields. Since Groovy creates a public field into a getter and setter under the hood, Spring sees the setter and Grails is configured to inject by name, so as long as the field/setter matches a Spring bean name it works.
So your controller should look something like this:
class MyController {
def exportService
def myAction = {
if (params?.format && params.format != "html") {
response.contentType = grailsApplication.config.grails.mime.types[params.format]
response.setHeader("Content-disposition",
"attachment; filename=books.${params.extension}")
exportService.export(params.format,
response.outputStream,projectsList, [:], [:])
}
}
}
Note that I also removed the use of ConfigurationHolder since it's deprecated. The best way to get access to the config is from the grailsApplication bean. It's already injected in controllers (and taglibs), and in a service or other bean you'd just need a def grailsApplication field declaration.
Want to fetch a value from message.properties file in grails in a job , how can I do that ??
My Job:
def execute() {
// execute task
List<String> emails = NayaxUser.findAllByEmailSent(false)*.username
emails.each {emailAddress->
mailService.sendMail {
//todo: FETCH FROM MESSAGE.PROPERTIES
to emailAddress
from FETCH FROM MESSAGE.PROPERTIES
subject FETCH FROM MESSAGE.PROPERTIES
html body.toString()
}
}
}
You can use:
g.message(code: 'my.message.code')
//or
g.message(code: 'my.message.code', args: [arg1, arg2])
You can inject the messageSource to retrieve the message:
class MyJob {
def messageSource
def execute() {
...
def message = messageSource.getMessage('message.code', ...)
...
}
}
Here's the documentation for getMessage(); you need to provide a couple more method arguments, namely args (an Object[]) and a Locale.
You can get a reference to the messageSource bean from anywhere using:
import org.codehaus.groovy.grails.commons.ApplicationHolder
import org.springframework.context.MessageSource
MessageSource messageSource = ApplicationHolder.application.mainContext.getBean('messageSource')
You can then get the messages themselves using the methods of the MessageSource interface.
Just as an addition to above answers, there could be following places where you need to implement internationalisation or fetch the message from message bundles.
views
controllers
services
Filters
Utility files(e.g. in util package or generalised exception message handling)
Special files e.g. in Shiro security rest realms
Below are the elaborate usage scenarios:
Views:- we have taglib available with message tag. Use this on views.
controllers :- message method is by default available here and locale conversion is automatically handled. See enter link description here
service: we may call taglibs inside services as below:
def myCustomTaglib = grailsApplication.mainContext.getBean('com.custom.MyCustomTagLib');
Or inject messageSource bean as
def messageSource
4.Filters / utility / Special files:- For these you may create something like below and then use it throughout.
String i18nMessage(def input,String defaultMessage) {
String[] languageCode = RequestContextHolder.currentRequestAttributes().request.getHeader("Accept-Language").split("-")
Locale locale = languageCode.length == 2 ? new Locale(languageCode[0], languageCode[1]) : new Locale(languageCode[0])
String message = defaultMessage
try {
message = messageSource.getMessage(input.code,input?.args?.toArray(),locale)
}catch (NoSuchMessageException nsme ){
log.info("No such error message--> ${nsme.getMessage()}")
}
return message
}
Also, if you get exception below:
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Then, you might need to add request listener to your web.xml
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
Note: web.xml is not available by default, you need to generate it from template.
These are the most common places you might need message bundle conversions.
Solution at point 4 would work in almost all cases. If you notice locale has been handled here manually which we could pass after fetching from requestHeader or request params optionally.
Hope it helps.
I have and WebFlow in my controller, and simple flow-scoped service. Somewhere close to the end of my web flow, I need to validate my command object field against value I received earlier in the web flow. For this I created a simple flow-scoped service:
class EventFlowService implements Serializable {
static transactional = false
static scope = "flow"
Date getEventStartDate(){
flow.basicData.eventDate
}
}
I don't need my service anywhere else than in a command object, so I inject it to my command object, like so:
class EventRestrictionsCommand implements Serializable{
def eventFlowService
boolean onlineRegistration
Date onlineRegistrationEnd
Date onlineRegistrationStart
static constraints = {
onlineRegistrationEnd validator: {val, obj ->
if(obj.onlineRegistration){
return val > obj.onlineRegistrationStart || val <= obj.eventFlowService.getEventStartDate()
}
return null
}
}
}
The problem is that I get exception saying, that there is no flow property in my service. Is there any way I can get access to flow storage in my flow-scoped service?
I met the SAME issue before and worked it out by installing webflow plugin in GRAILS:
grails install-plugin webflow
Say, the new version of grails surports webflow well by installing webflow plugin.