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
}
Related
I am new with grails and am developing a web application.
I have a List of long values which is getting from ids of Domain class objects.
Initially this List is like [1,2,3]. I need to use this list of values in my service class for saving the associations.
But the same List is getting in service class as [49,50,51]
Why this difference of 48 is occurred? and how can i get the List as same as i sent.
Controller class:
def createQuestion(CreateQuestionCommand createQuestionCmd) {
if( createQuestionCmd.hasErrors() ) {
render(view:"create_question", model:[createQuestionCmd:createQuestionCmd , tags:Tag.list()])
} else {
Question question = new Question()
question.title=createQuestionCmd.title
question.description=createQuestionCmd.description
List tags= createQuestionCmd.tags
question = questionService.create(question,tags)
render(view: "question_submitted")
}
}
service class:
def create(Question question, List<Long> tagId) {
List<Tag> tagList=getTagsById(tagId)
question.save( failOnError:true )
Iterator itr=tagList.iterator();
while(itr.hasNext()) {
Tag tag=itr.next()
question.addToTags(tag).save()
}
}
def getTagsById(List tagId){
Iterator itr=tagId.iterator();
List<Tag> tags
while(itr.hasNext()) {
long id=itr.next()
println "value of id is : "
println id
println id.getClass().getName()
Tag tag=Tag.findById(id)
tags.add(tag)
}
return tags
}
CreateQuestionCmd.tags are List<String> and you are trying to place that to List<Long>
Just pass the object to service and create question object.In groovy we create the object in map format.In java only, we need iterator to loop collection.In groovy we use each closure to loop the collection. Try it will work out. Any problem in the following code let me know.I will help.
Controller class:
def createQuestion(CreateQuestionCommand createQuestionCmd) {
if(createQuestionCmd.hasErrors() ) {
render(view:"create_question", model:[createQuestionCmd:createQuestionCmd , tags:Tag.list()])
} else {
questionService.create(createQuestionCmd)
render(view: "question_submitted")
}
}
service class:
def create(def createQuestionCmd) {
def question = new Question(title:createQuestionCmd.title,description:createQuestionCmd.description)
question.save(flush:true)
List tagIds= createQuestionCmd.tags
tagIds.each{
def tag=Tag.findById(id)
question.addToTags(tag).save(flush:true)
}
}
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)
}
I want to call a subflow where the controller is not known. It is passed in parameters to beginFlow and I save that in flow scope. Inside goToForm I'd like to call use the controller that is saved in flow.theController.
def beginFlow = {
enter {
action {
if (params?.redirectTo != null) {
String flow.theController = params.redirectTo
}
if ( flow.theController() ) {
success()
}
}
on("success").to("beginPage")
}
beginPage {
on('next').to('goToForm')
}
goToForm {
// I'd like this:
// subflow(controller: flow.theController, action:'start'
// this subflow works, but won't work for all cases
subflow(controller: 'AZ_A4', action:'start')
on('done').to('showResults')
on('notDone').to('beginPage')
}
showResults {
redirect(action: 'results')
}
}
As discussed on the user list, it appears that this isn't possible directly, as the subflow name has to be known at the time when the flow structure is being built (at application startup). But since the flow definition DSL is Groovy code you can do something like this:
beginPage {
on('next').to('selectSubflow')
}
selectSubflow {
action {
return "subflow_${flow.theController}"()
}
for(subController in listOfControllers) {
on("subflow_${subController}").to("subflow_${subController}")
}
}
for(subController in listOfControllers) {
"subflow_${subController}" {
subflow(controller:subController, action:'start')
on('done').to('showResults')
on('notDone').to('beginPage')
}
}
The listOfControllers could be a static somewhere, or you could possibly do something like this at the top of the flow definition
def beginFlow = {
def listOfControllers = grailsApplication.controllerClasses.findAll {
it.flows.containsKey('start')
}.collect { it.logicalPropertyName }
enter {
// ...
to enumerate all the controllers in the application that define a startFlow. You might need a def grailsApplication in your class, I always forget which places in Grails have it available by default and which don't...
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])}
Lets assume that I have the following configuration in my conf/InjectionConfig.groovy file:
x {
a = { attrs, body -> out << "hello" }
b = { attrs, body -> out << "goodbye" }
}
and that I have a simple taglib such as
class XTagLib {
static namespace = "x"
}
What I want to do is that when I type <x:a /> to any of my views, it would print hello. I've already tried to inject these to the metaclass of the taglib as both property and method but neither seem to work. As an example, here's basically what I'm doing right now in a service:
public void afterPropertiesSet() throws Exception {
GroovyClassLoader classLoader = new GroovyClassLoader(getClass().classLoader)
def slurper = new ConfigSlurper(GrailsUtil.environment)
ConfigObject xConfig
try {
xConfig = slurper.parse(classLoader.loadClass('InjectionConfig'))
}
catch (e) {
e.printStackTrace()
}
xConfig.x.each({
if ( !XTagLib.metaClass.hasMetaProperty(it.key) ) {
XTagLib.metaClass.registerBeanProperty(it.key, { args ->
def attrs = args[0], body = args[1]
it.value.call(attrs, body)
}
}
})
}
Am I just doing it wrong or is this even possible currently?
Well, this
def shell = new GroovyShell() // or get a GroovyClassLoader
Class yTagLibClass = shell.evaluate("class YTagLib { static namespace = 'x' }; return YTagLib")
yTagLibClass.metaClass.a = { attrs, body -> delegate.out << 'blabla' }
grailsApplication.addArtefact(TagLibArtefactHandler.TYPE, yTagLibClass)
<x:a/> nearly worked for me - registered a tag, except for it didn't output anything. You still need to make the closure resolve out against Grails' taglib's out property.
I don't see a pretty way to do it, as there's no access to instance variables, and out is an instance variable. See Grails source, JspInvokeGrailsTagLibTag.doStartTagInternal() - you might find a way.
EDIT: I added delegate. prefix that should resolve out property of target object. Now I believe I deserve an acceptance :)
What I want to do is that when I type
to any of my views, it would
print hello
I think there's an alternative way to do what you intend: combine template & tagLib. First, create a template, then add it in your TagLib (with no complex configuration).
In my opinion, it's more simple than your approach.
Please take a look at this tutorial:
http://jan-so.blogspot.com/2008/02/example-of-template-and-taglib-with.html