For some reason I get a huge list of errors when using following code:
class UrlMappings {
static grailsApplication
static mappings = {
grailsApplication.controllerClasses.each { controllerClass -> // FAILS!
println(controllerClass.name)
}
"/$controller/$action?/$id?"{}
"/"(view:"/index")
"500"(view:'/error')
}
Errors: http://pastebin.com/tiEsENie
Where as following code works just fine and prints all the controller names:
class UrlMappings {
static grailsApplication
static mappings = {
"/$controller/$action?/$id?"{
grailsApplication.controllerClasses.each { controllerClass -> // WORKS!
println(controllerClass.name)
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
Isn't it possible to access the static grailsApplication from inside static mappings?
(I need to be able to get the controller names in order to dynamically create urlmappings)
While ApplicationHolder still works, the grails docs state this for the in the deprecation comments
deprecated: Use dependency injection or implement GrailsApplicationAware instead
Since grailsUrlMappingsHolderBean implements GrailsApplicationAware, I found that the code below works in 2.0 as well
class UrlMappings {
static mappings = {
getGrailsApplication().controllerClasses.each{ controllerClass ->
if(controllerClass.name./*your logic here*/){
"/mod/action" {
controller = "${controllerClass.name}"
}
}
}
}
}
Related
My grails project has at least 20 controllers each with at least 4 methods each, and it is continuing to grow. I decided to annotate every method like this:
import enums.URLMapped
class HomeController {
#URLMapped(url="/", alias="home")
def landingPage() {
render(view: "/index")
}
}
How can I append this URL value inside the URLMapping.groovy dynamically? I could do something like:
import enums.URLMapped
import java.lang.reflect.Method
import org.apache.commons.lang.StringUtils
import org.codehaus.groovy.grails.commons.DefaultGrailsControllerClass
import org.codehaus.groovy.grails.commons.GrailsApplication
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
// My annotation crawler/parser
for(DefaultGrailsControllerClass controller in GrailsApplication.getControllerClasses()) {
for(Method method in controller.getClazz().getDeclaredMethods()) {
if(!method.isSynthetic()) {
if(method.isAnnotationPresent(URLMapped.class)) {
URLMapped url = method.getAnnotation(URLMapped.class)
name "${url.alias()}": "${url.url()}" {
action="${method.getName()}"
controller="${StringUtils.capitalize(controller.getClazz().getSimpleName().split(/Controller/).getAt(0)}"
}
/* What I want to achieve above is this static URL:
name home: "/" {
action="landingPage"
controller="Home"
}
*/
}
}
}
}
}
}
But the problem is that static mapping is a closure and you shouldn't suppose to loop (?) inside it. So how can I add my URL? I could always put all static URL for all of our links here, it's just getting tedious to maintain.
I would strongly recommend you get rid of this idea with annotations but to divide your project into several plugins (each - a separate grails-app) tied together with gradle.
Some useful info:
https://docs.gradle.org/current/userguide/custom_plugins.html
https://docs.gradle.org/current/userguide/multi_project_builds.html
grails 2.3.8 here. When i g:link to certain controller actions, i want to put a namespace in the url in front of the controller name.
For example:
call "app/apple/eat" -> "app/admin/apple/eat"
Since there are many dynamic and static scaffolded controllers involved, I thought i can do this with some UrlMappings expression but i dont know how to do this.
I tried out something like this without no success:
static mappings = {
"/apple/$action?/$id?" (redirect:"/admin/apple/$action?/$id?")
}
using namespace = "admin" in the AppleController doesnt work aswell
thanks for advices
For the example
call "app/apple/eat" -> "app/admin/apple/eat"
you could do it via the grails-app/conf/UrlMappings.groovy with the following configuration (which works than for the whole application):
class UrlMappings {
static mappings = {
"/admin/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
with this configuration, your controllers are reachable via:
http://localhost:8080/apple/admin/apple/eat/1
etc.
Or!
If you would only have apple under the admin prefix, than you could do the following in grails-app/conf/UrlMappings.groovy:
class UrlMappings {
static mappings = {
"/admin/apple/$action?/$id?"(controller : "apple")
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
Or!
If you have more redirects, you could group them together:
static mappings = {
group("/admin") {
"/apple/$action?/$id?(.${format})?"(controller: 'apple')
"/peach/$action?/$id?(.${format})?"(controller: 'peach')
}
...
I would like to utilize a service written in Java in my Grails app using dependency injection. Creating it in Java without injection would look like this:
ServiceFactory.newInstance().getElementService()
I would like to use this in the same way services are injected for controllers, services, and jobs.
class ImportJob {
def elementService
...
}
I know this should go into resources.groovy and this is what I have so far:
serviceFactory(ServiceFactory) { bean ->
bean.factoryMethod = 'newInstance'
}
elementService(ElementService) {
}
I have found few resources in the documentation to help with this. How do I complete the elementService so it is creating the object as described above? Should I use a BeanBuilder?
You could create a FactoryBean for this since it's not as direct as calling a method in a class:
package com.mycompany
import org.springframework.beans.factory.FactoryBean
import org.springframework.beans.factory.InitializingBean
class ElementServiceFactoryBean implements FactoryBean<ElementService>, InitializingBean {
private ElementService elementService
ElementService getObject() { elementService }
Class<ElementService> getObjectType() { ElementService }
boolean isSingleton() { true }
void afterPropertiesSet() {
elementService = ServiceFactory.newInstance().elementService
}
}
and you'd register it in resources.groovy as
elementService(ElementServiceFactoryBean)
Use bean.factoryMethod and bean.factoryBean in the elementService bean.
serviceFactory(ServiceFactory) { bean ->
bean.factoryMethod = 'newInstance'
bean.scope = 'singleton'
}
elementService(ElementService) { bean ->
bean.factoryMethod = 'getElementService'
bean.factoryBean = 'serviceFactory'
}
This is a simple solution especially if ServiceFactory is external and cannot be changed.
I'd like to use data generator at init in my application. It works fine when create objects using .save() method, but it doesn't work when I want to use dedicated services, because of null pointers instead of injected services. That's my code:
I have defined DataGenerator bean inside conf/spring
beans = {
dataGenerator(DataGenerator)
}
My Bootstrap.groovy looks like:
class BootStrap {
def dataGenerator
def init = { servletContext ->
dataGenerator.generateData()
}
}
In `DataGenerator' I have:
class DataGenerator{
BookService bookService
def generateData() {
log.info("Generating books")
createBooks()
}
def createBooks(){
(1..40).each() {
CreateBookCommand command = new CreateBookCommand()
/* some command populate code*/
bookService.create(command);
}
}
}
The problem is, that I cannot invoke create() method, because bookService is always null
BookService is simple grails service with some dependencies, of course placed in grails-app/services
class BookService {
UserService userService
SpringSecurityService springSecurityService
def create(CreateBookCommand command){
Book book = new Book()
command.bindTo(book)
book.save(flush:true, failOnError:true)
}
/*some other methods*/
}
Could you tell me how to fix it?
Try this in resources.groovy
beans = {
dataGenerator(DataGenerator) { bean ->
bean.autowire = 'byName'
}
}
I assume DataGenrator being a class outside the grails artifact (that is: placed in src/groovy), you can refer the already available service class in the context as:
beans = {
dataGenerator(DataGenerator){
bookService = ref('bookService')
}
}
or try autowiring byName as mentioned by #sudhir.
I am currently updating my Grails project in order not to use the deprecated ConfigurationHolder class.
This goes fine in most cases, but I am facing trouble in my custom codec classes, where I have been using the following approach until now:
import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH
class MyCodec {
static boolean myStaticConfigProperty=CH.config.myStaticConfigProperty
static encode = { something ->
if(myStaticConfigProperty)
...
}
}
Direct injection using
def grailsApplication
does not work in this case since this will be injected as a non-static object.
Instead I have tried to use the approach suggested in this post getting-grails-2-0-0m1-config-info-in-domain-object-and-static-scope, but I cannot make it work even after injecting the grailsApplication object into my codec metaclasses in the bootstrap:
class BootStrap {
def grailsApplication
def init = { servletContext ->
for (cc in grailsApplication.codecClasses) {
cc.clazz.metaClass.getGrailsApplication = { -> grailsApplication }
cc.clazz.metaClass.static.getGrailsApplication = { -> grailsApplication }
}
}
}
Could anyone suggest an approach that will allow me to access the config object in a static way inside codec classes?
I'd suggest something like this completely untested code:
class MyCodec {
static def grailsConfig
static boolean myStaticConfigProperty = grailsConfig.myStaticConfigProperty
static encode = { something ->
if(myStaticConfigProperty)
...
}
}
class BootStrap {
def grailsApplication
def init = { servletContext ->
for (cc in grailsApplication.codecClasses) {
cc.grailsConfig = grailsApplication.config
}
}
}
If all of your codec classes just need the same one configuration property, you could skip injecting the grailsApplication and/or the config object entirely, and just set the one static property from BootStrap.
it works for me in grails 2.2.3
import grails.util.Holders as holders;
class MyFileCodec {
static encode = {file ->
def configPath= holders.grailsApplication.config.share.contextPath
return "${configPath}/${file.name}"
}
}
grails.util.Holders has been introduced since grails 2.0, it's the way to access config object.