I'm trying to consolidate redundant code in my controller using a utility closure to do generic exception handler and response generation.
For instance, I want to consolidate this:
def newUser(){
def model = [:]
def errors
try{
model += [newUserObj:dao.newUser(...)]
}catch(Exception e){
errors = e.getMessage()
}
renderJson(model,error)
}
..to this:
def newUser(){
def model = [:]
def errors
doRequest(model, errors){ ->
model += [newUserObj:dao.newUser(...)]
}
}
..while moving boilerplate code to a closure:
def doRequest(model, errors, clsr){
try{
clsr.call()
}catch(Exception e){
errors = e.getMessage()
}
//!! model here is null !!
renderJson(model,error)
}
The problem happens in doRequest() when renderJson() is invoked; model is null, even though I confirmed it's assigned correctly inside the closure at model += [newUserObj:dao.newUser(...)].
Workaround
I managed to get around this issue by just returning the model from the closure:
def newUser(){
doRequest(){ ->
def model = [:]
def errors
model += [newUserObj:dao.newUser(...)]
[model:model, errors:errors]
}
}
def doRequest(clsr){
def model = [:]
def errors
try{
def r = clsr.call()
model = r['model']
errors = r['errors']
}catch(Exception e){
errors = e.getMessage()
}
renderJson(model,error)
}
..but this doesn't seem Groovy at all, I'm creating boiler plate code I was trying to avoid in the first place.
Is this what you were looking for?
import grails.converters.JSON
//Closure implementation
def doRequest(Closure clsr) {
def model = [:]
def errors = /No Error Message Yet/
try {
model = clsr(model)
} catch(Exception e) {
errors = e.getMessage()
}
renderJson(model, errors)
}
//Mimics an action method
def newUser() {
doRequest { model ->
model += [a:1] //Mimics the call to DAO in your question
//make sure to return the model after all operations completed
//model
}
}
//Mimics the render to JSON utility
private JSON renderJson(model, error) {
[model: model, errors: error] as JSON
}
//Mimics call to the action method
assert newUser().toString() ==
/{"model":{"a":1},"errors":"No Error Message Yet"}/
I could have implemented doRequest() something as below, but I did not because in that case model and errors would become part of the class (in your case it will be Controller's global properties) which we do not want.
def doRequest(Closure clsr) {
try {
clsr.resolveStrategy = Closure.DELEGATE_FIRST
clsr.delegate = this
clsr()
} catch(Exception e) {
errors = e.getMessage()
}
renderJson(model, errors)
}
Related
I have a simple domain
class Cat {
String name
static constraints = {
}
}
The controller is as follows:
class CatController {
def index() { }
def say(){
def cat = new Cat(name: "cc")
cat.save()
render "done"
}
}
and i have a test integration test as follows:
class CatTests extends GroovyTestCase{
CatController controller = new CatController()
#Test
void testSomething() {
Cat.metaClass.save = {
throw new Exception("Asdasd")
}
shouldFail(Exception){
controller.say()
}
GroovySystem.metaClassRegistry.removeMetaClass(Cat.class)
}
#Test
void testSomething2() {
def before_cat_count = Cat.count()
controller.say()
def after_cat_count = Cat.count()
assertEquals after_cat_count - before_cat_count, 1
}
}
I get the following error when running the test.
Failure: testSomething2(cat.CatTests)
| groovy.lang.MissingMethodException: No signature of method: cat.Cat.count() is applicable for argument types: () values: []
Possible solutions: count(), ident(), print(java.lang.Object), print(java.io.PrintWriter), getCount(), wait()
at cat.CatTests.testSomething2(CatTests.groovy:37)
| Completed 2 integration tests, 1 failed in 192ms
Now, my doubt and question is why is it not finding the count method on this line
def before_cat_count = Cat.count()
of testSomething2() method.
I have already removed the meta class at the end of method testSomething. So, i am wondering why is count() method not being found. I appreciate any help! Thanks!
Perhaps simply
GroovySystem.metaClassRegistry.removeMetaClass(Cat)
simply Cat, not Cat.class
Sourced from the web, I use a method for this type of cleanup.
def revokeMetaClassChanges(Class type, def instance = null) {
GroovySystem.metaClassRegistry.removeMetaClass(type)
if(instance) {
instance.metaClass = null
}
}
With invocations like:
cleanup:
revokeMetaClassChanges(FirstService, firstServiceInstance)
revokeMetaClassChanges(SecondService, null)
with much success.
Where should the separation of the UI message elements be if a grails service throws an exception? Should the message get loaded by the service and passed to the controller via the exception, or should the controller load the message based on the type of exception thrown? This assumes the message will have some parameter values that need to be filled in.
Here is an exception:
class CustomException extends RuntimeException {
String message
}
Loading the message source from the controller after catching the exception:
class MyService {
void doSomething() {
...
if (somethingBad) {
String value = 'Mary Smith'
throw new CustomException(value)
}
...
}
}
class MyController {
def myService
void processRequest() {
try {
myService.doSomething()
}
catch (CustomException e) {
flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
render view:'some_gsp'
}
...
}
}
Loading error from message source in the service where the controller pulls the message string the from the exception:
class MyService {
def messageSource
void doSomething() {
...
if (somethingBad) {
String value = 'Mary Smith'
throw new CustomException(messageSource.getMessage('thread.inactive.user', [value]))
}
...
}
}
class MyController {
def myService
void processRequest() {
try {
myService.doSomething()
}
catch (CustomException e) {
flash.error = e.message
render view:'some_gsp'
}
...
}
}
Frankly speaking, neither of those two places do you need the translations. :)
Separation Of Concern
Controller should only worry about HTTP methods and its delegation.
Services should take care of transactions and underlying business logic.
Declarative Error Handling
For 2.0.* and above, Grails provides you a sweet spot for handling errors. Guess what? Declarative Error Handling
All exception related code moves to a separate controller (in house) where they are handled properly, keeping your business controllers and services clean and abstracted from boiler plate codes.
For Grails 2.3.*, an added feature was to handle exception in the controller itself but most of the boiler plate (try catch stuff) is abstracted from the controller implementation.
Conclusion
If you are using v2.0.* and above then your controllers would look something like:
class MyController {
def myService
def processRequest() {
myService.doSomething()
...
}
}
//URL Mapping
static mappings = {
"500"(controller: "errors", action: "customException",
exception: CustomException)
}
//Error Controller
class ErrorsController {
def customException() {
def exception = request.exception
// perform desired processing to handle the exception
}
}
You can move the logic of error handling to a separate plugin if required in order to handle variety of errors/exception and unhappy paths. It becomes elegant to separate this concern.
If you are using v2.3.* then your controller would look something like:
class MyController {
def myService
def processRequest() {
myService.doSomething()
...
}
def handleCustomException(CustomException e) {
//Move this translation to src/groovy/utility if feasible
flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
render view:'some_gsp'
}
}
In this case no handling required from services as well, you just need to throe that exception.
I suppose you would get more input from various sources as well if you look around and are interested to use this pattern.
I have a Grails project and I need to send/save table data. I have a controller(doesn't have views) with following code.
class JsonController {
def getCompany = {
for (String s in request.getHeaderNames()) {
println request.getHeader(s)
}
println Company.list()
render Company.list() as XML
}
def getEmployees = {
for (String s in request.getHeaderNames()) {
println request.getHeader(s)
}
render Employees.list() as XML
}
def getManagers = {
for (String s in request.getHeaderNames()) {
println request.getHeader(s)
}
render Managers.list() as XML
}
}
Now I need to call/run these functions on clicking an link and send the output thru email or save to a folder. How can I do this?
Thankyou
Note: the following code requires the mail plugin
I would move your JsonController to a service, which I renamed to XMLService, since that is what you are returning. Then, inject the XMLService in whatever artefacts you want to use it in, such as another service or controller.
import grails.converters.XML;
class XMLService {
def getCompanies = {
return Company.list() as XML
}
def getEmployees = {
return Employees.list() as XML
}
def getManagers = {
return Managers.list() as XML
}
}
Then, in a controller or service. This example uses a service:
class MyMailingService {
def mailService //<-- included from the Mail plugin
def xmlService
void sendEmployeeList(){
mailService.sendMail {
to "fred#g2one.com","ginger#g2one.com"
subject "Hello to mutliple recipients"
body xmlService.getEmployees()
}
}
}
And for storing files, something like the following. Note, that this does not have to be service, but for demonstration purposes, it was easier.
class MyStorageService {
def xmlService
void storeEmployeeList(){
//It was a little unclear how you wanted to store the file, so be careful because this this does not include checks you would want to implement in production (e.g. checks for existing files, possible runtime exceptions, etc).
def f= new File('employees.txt') //see http://docs.codehaus.org/display/GROOVY/JN2015-Files
f << xmlService.getEmployees()
}
}
Noticed you have had answers for email text file - this is how to to store to XML file
where rowid will be the definition for each xml row
def file=""${System.properties['catalina.base']}/file.xml"
try {
new File(file).withWriter { writer ->
def xml = new MarkupBuilder( writer )
def Users = Registeration.list()
xml.mkp.xmlDeclaration(version: "1.0", encoding: "utf-8")
xml.rowid {
Employees.each { employee ->
xml.registeration(id: employee.id) {
username(employee.username)
//somethingelse(employee.somethingelse)
}
}
}
}
} catch (Exception e) {
result=e.printStackTrace()
}
if (result!=null) {
result="all done stored in "+file
}else{
result="Something has gone wrong with "+file
}
I needed to display some validation messages received from the service each in its proper place, and I solved it putting the messages in an exception:
class InvalidInputException extends RuntimeException {
def errors
InvalidInputException(String s) {
super(s)
}
InvalidInputException(String s, errors) {
super(s)
this.errors = errors
}
}
That way, I could throw the exception sending the errors:
if (errors) {
throw new InvalidInputException("There were some errors", errors)
}
.. and then I deal with the errors later in the controller, after catching the exception:
...
catch (InvalidInputException e) {
if (e.errors) {
// set them up to display appropriately
}
// render the view
}
Now, I've read somewhere that Groovy's exceptions can cost too much, so... is this too bad?
What problems may I encounter putting additional data in an Exception?
It's much easier than fiddling with returned error messages, and the code is much shorter.
If you are concerned about the performance of exceptions in Java, I suggest you to look at this other question.
If you not create a exception, the other possibility is to make your service return an object that represents the result of this flow. Something like:
class MyServiceResult {
List<String> errorCodes = [] //codes for i18n
void addErrorCode(String errorCode) {
errorCodes << errorCode //add error to list
}
boolean isValid() {
return (!(errorCodes.size() > 0)) //if we have errors then isn't valid.
}
List<String> getErrorCodes() {
return errorCodes.asImmutable()
}
}
And just use it in your service method
class MyService {
MyServiceResult someMethod() {
MyServiceResult result = new MyServiceResult()
...
result.addErrorCode('some.key.here')
...
return result
}
}
class MyController {
def myService
def action() {
MyServiceResult result = myService.someMethod()
if(!result.isValid()) {
//handle errors
}
}
}
But it's important to say that it can be 2x slower than creating an exception. You can check all details in this post.
Background:
I have grails 1.3.7 application which uses g:createLink and g:link on many pages.
Recently I decided to make big change in url mappings - introduce preceding path element.
Currently I have: /$controller/$action?/$id?
But want to have: /$regionId/$controller/$action?/$id?
It was easy to change urlMappings, but I can't figure out how to easily change the behavior how links are built throught the application.
Basically, I don't want to go through each page and change links. But want to do this in one place.
Question
How to override ApplicationTagLib#createLink functionality so grails will use this implementation without the need of changes pages which use this tag (or function)?
Any help greatly appriciated!
I had a smilar problem. Actually you can decorate g:link tag like this.
1) TagLib
import org.codehaus.groovy.grails.plugins.web.taglib.*
class OverrideDefaultTagLib {
static namespace = "g"
def link = { attrs, body ->
def c = "1" // Get it from session or somewhere else
if (attrs.params) {
attrs.params.put("region", c)
} else {
attrs.params = [region: c]
}
def applicationTagLib = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
applicationTagLib.link.call(attrs, body)
}
}
}
2) add to UrlMappings.groovy
"/$region/$controller/$action?/$id?"{}
I was unable to solve this problem in terms of OOP. I mean I can't find way how to override closure. I tried several approaches, but with no success. And documentation says that you can't override closure, you can only replace it with new implementation (please correct me if I wrong).
But (!) I was able to solve task by copy-pasting source code of ApplicationTagLib#createLink method.
I think this is brutal solution, but after 8 hours of fighting with this simple task - it's acceptable.
So finally all I need to do - is define this class, grails will immediately use it for link generation (for all views, no need to change their code):
import java.text.SimpleDateFormat;
import groovy.time.*;
import java.text.*;
import org.codehaus.groovy.grails.commons.GrailsControllerClass
import org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib;
import org.codehaus.groovy.grails.web.mapping.UrlCreator
import org.codehaus.groovy.grails.commons.ControllerArtefactHandler
import org.springframework.web.context.request.RequestContextHolder
class OverrideTagLib extends ApplicationTagLib {
def createLink = { attrs ->
// get value for regionId parameter
def regionId = regionIdFinderService.currentRegionId
// add cutsom regionId parameter
if (attrs) {
if (attrs.params)
attrs.params.put("regionId", regionId);
else {
attrs.params = ["regionId":regionId];
}
}
// process
def writer = getOut()
// prefer URI attribute
if (attrs.uri) {
writer << handleAbsolute(attrs)
writer << attrs.uri.toString()
}
else {
// prefer a URL attribute
def urlAttrs = attrs
if (attrs.url instanceof Map) {
urlAttrs = attrs.remove('url').clone()
}
else if (attrs.url) {
urlAttrs = attrs.remove('url').toString()
}
if (urlAttrs instanceof String) {
if (useJsessionId) {
writer << response.encodeURL(urlAttrs)
}
else {
writer << urlAttrs
}
}
else {
def controller = urlAttrs.containsKey("controller") ? urlAttrs.remove("controller")?.toString() : controllerName
def action = urlAttrs.remove("action")?.toString()
if (controller && !action) {
GrailsControllerClass controllerClass = grailsApplication.getArtefactByLogicalPropertyName(ControllerArtefactHandler.TYPE, controller)
String defaultAction = controllerClass?.getDefaultAction()
if (controllerClass?.hasProperty(defaultAction)) {
action = defaultAction
}
}
def id = urlAttrs.remove("id")
def frag = urlAttrs.remove('fragment')?.toString()
def params = urlAttrs.params && urlAttrs.params instanceof Map ? urlAttrs.remove('params') : [:]
def mappingName = urlAttrs.remove('mapping')
if (mappingName != null) {
params.mappingName = mappingName
}
if (request['flowExecutionKey']) {
params."execution" = request['flowExecutionKey']
}
if (urlAttrs.event) {
params."_eventId" = urlAttrs.remove('event')
}
def url
if (id != null) params.id = id
def urlMappings = applicationContext.getBean("grailsUrlMappingsHolder")
UrlCreator mapping = urlMappings.getReverseMapping(controller,action,params)
// cannot use jsessionid with absolute links
if (useJsessionId && !attrs.absolute) {
url = mapping.createURL(controller, action, params, request.characterEncoding, frag)
def base = attrs.remove('base')
if (base) writer << base
writer << response.encodeURL(url)
}
else {
url = mapping.createRelativeURL(controller, action, params, request.characterEncoding, frag)
writer << handleAbsolute(attrs)
writer << url
}
}
}
}
}
add regionId to params in createLink and g:link and grails is smart enough to match your urlmappings. i.e
${createLink(controller:'c',action:'a',id:1,params:[regionId:2])}