Why doesn't this generic usage work in groovy? - grails

Learning Groovy and Grails and I am trying to simplify some controllers by making a BaseController.
I define the following:
class BaseController<T> {
public def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond T.list(params), model:[instanceCount: T.count()]
}
}
Then I have the following:
class TeamController extends BaseController<Team> {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
/*
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Team.list(params), model:[teamInstanceCount: Team.count()]
}
*/
}
Every time I try to make a call to this, I get a MethodMissingException on T.count(). Why does Team.count() work, but when I try to use generics T.count() fail?
-- edit -- (Adding exception)
No signature of method: static java.lang.Object.count() is applicable for argument types: () values: []

T is a type. So this does not work as you expect it to be. You have to hold a concrete class (see Calling a static method using generic type).
So in your case it's easiest to also pass down the real class. E.g.:
class A<T> {
private Class<T> clazz
A(Class<T> clazz) { this.clazz = clazz }
String getT() { T.getClass().toString() }
// wrong! String getX() { T.x }
String getX() { clazz.x }
}
class B {
static String getX() { return "x marks the place" }
}
class C extends A<B> {
C() { super(B) }
}
assert new C().x=="x marks the place"

Related

Get class name of class

Groovy beginner, coming from Java / Kotlin, how do I get the class name of an (anonymous) implementation of my class?
Failed attempts:
abstract class Foo {
String name() { this.class.simpleName }
}
abstract class Foo {
String name() { return this.class.simpleName }
}
abstract class Foo {
String name() { return getClass().getSimpleName() }
}
abstract class Foo {
String name() { this.metaClass.classNode.nameWithoutPackage }
}
So obviously I seem to give some class instance a default name. I thought "well, if class name does not work, let's try individual naming" with this:
abstract class Foo {
private final AtomicInteger counter = new AtomicInteger(0)
String name() { "number " + this.counter.incrementAndGet() }
}
but this doesn't work either because counter is not a property of groovy.lang.Binding.
This is all in context of a Jenkins pipeline I try to write... why is this so hard?
You probably want this...
abstract class Foo {
String name() { this.class.name }
}

Grails binddata in service

Is there a way to utilize bindData in a service other than using the deprecated BindDynamicMethod? I can't just use
TestObject testObject = new TestObject()
TestObject testObject.properties = params
or
TestObject testObject = new TestObject(params)
because I have a custom bind method utilizing the #BindUsing annotation within my TestObject class.
If you are using Grails 3.* then the service class can implement DataBinder trait and implement bindData() as shown below example:
import grails.web.databinding.DataBinder
class SampleService implements DataBinder {
def serviceMethod(params) {
Test test = new Test()
bindData(test, params)
test
}
class Test {
String name
Integer age
}
}
This is how I quickly tried that in grails console:
grailsApplication.mainContext.getBean('sampleService').serviceMethod(name: 'abc', age: 10)
In Grails 2.4.4 you can do something like this:
// grails-app/services/demo/HelperService.groovy
package demo
import org.grails.databinding.SimpleMapDataBindingSource
class HelperService {
def grailsWebDataBinder
TestObject getNewTestObject(Map args) {
def obj = new TestObject()
grailsWebDataBinder.bind obj, args as SimpleMapDataBindingSource
obj
}
}
In 2.5, I found that emulating the behaviour of the Controller API in a helper service worked:
def bindData(def domainClass, def bindingSource, String filter) {
return bindData(domainClass, bindingSource, Collections.EMPTY_MAP, filter)
}
def bindData(def domainClass, def bindingSource, Map includeExclude, String filter) {
DataBindingUtils
.bindObjectToInstance(
domainClass,
bindingSource,
convertToListIfString(includeExclude.get('include')),
convertToListIfString(includeExclude.get('exclude')),
filter);
return domainClass;
}
convertToListIfString is as per the controller method:
#SuppressWarnings("unchecked")
private List convertToListIfString(Object o) {
if (o instanceof CharSequence) {
List list = new ArrayList();
list.add(o instanceof String ? o : o.toString());
o = list;
}
return (List) o;
}

Passing an instance of a class in Groovy using generics

Grails: 2.4.4
Groovy: 2.4.1
Java : 1.8u40
Windows 7
I am trying to make a generics based controller that members of my team can extend as needed.
Having just getting this issue solved in Groovy with generics ( Why doesn't this generic usage work in groovy?)
I am now running into the following issue in a Grails controller trying to pass an instance of the class.
The TeamCotroller:
class TeamController extends BaseController<Team> {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
TeamController() {
super(Team)
}
/*
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Team.list(params), model:[teamInstanceCount: Team.count()]
}
*/
/*
def show(Team teamInstance) {
respond teamInstance
}
*/
/*
def create() {
respond new Team(params)
}
*/
/* More default CRUD methods cut out for now */
}
The Generic Controller:
class BaseController {
private final Class<T> clazz
BaseController(Class<T> clazz) {
this.clazz = clazz
}
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond clazz.list(params), model:[instanceCount: clazz.count()]
}
// TODO: Figure this out
def show(Team instance) {
respond instance
}
}
In the show(Team instance) method, I have replaced Team with Class<T> and T in an attempt to get the instance being passed to it by Grails/Groovy but it doesn't even seem to be hitting the method at all when I run in debug mode. What does one need to do in order to get the instance being passed to the controller?
--edit-- Adding Exception
2015-03-06 15:56:36,400 [http-bio-8090-exec-5] ERROR errors.GrailsExceptionResolver - MissingPropertyException occurred when processing request: [GET] /test-list/team/show/1
No such property: instance for class: TeamController. Stacktrace follows:
Message: No such property: instance for class: TeamController
-- Edit -- Adding controller code
It doesn't seem to work when passing the instance directly into the parameters of the method, but it does work as expected when using the id parameter, like so:
grails-app/controllers/BaseController.groovy
abstract class BaseController<T> {
private final Class<T> clazz
BaseController() {}
BaseController(Class<T> clazz) {
this.clazz = clazz
}
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond clazz.list(params), model:[instanceCount: clazz.count()]
}
def show(Long id) {
def instance = clazz.get(id)
respond instance
}
}
grails-app/controllers/TeamController.groovy
class TeamController extends BaseController<Team> {
TeamController() {
super(Team)
}
}
Then you should be able to access the /team/show/1.?{format} endpoint without an issue.
I setup an example project here to demonstrate.

Cannot invoke method render() on null object in Grail while email sending

I am using mail:1.0.1 plugin for mail sending
but while sending mail its gives me an error..
Source :-
def serviceMethod(EmailModel mailObj) {
PageRenderer groovyPageRenderer;
try{
sendMail {
to "abc#gmail.com"
subject mailObj.subject;
html groovyPageRenderer.render(template:"myMailTemplate", model: [mailObj: mailObj])
}
} catch (Throwable th) {
th.printStackTrace();
}
}
If you want to send the gsp page as email body then you can send it like:
def mailService
def serviceMethod(EmailModel mailObj) {
...
mailService.sendMail {
to email
subject "subject"
body(view: "/_template", model: [mailObj: mailObj])
}
...
}
EDIT...................................................................................
Just inject PageRenderer groovyPageRenderer globally, like
import grails.gsp.PageRenderer
class TestService {
PageRenderer groovyPageRenderer
def getText() {
String s = groovyPageRenderer.render(template: "../first/temp", model: [name: 'user1690588'])
println "Content = ${s}"
}
}
I think you are calling Service Class(.groovy) method from java class.
by using object of EmailService class.
So you cant get Object of PageRenderer class.
for this
Create SpringsUtil Class in src/java and define constant object of EmailSerevice. like this
public class SpringsUtil {
public static ApplicationContext getCtx() {
return getApplicationContext();
}
public static ApplicationContext getApplicationContext() {
return (ApplicationContext) ServletContextHolder.getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT);
}
#SuppressWarnings("unchecked")
public static <T> T getBean(String beanName) {
return (T) getApplicationContext().getBean(beanName);
}
public static final String EMAIL_SERVICE = "emailService";
// public static final String INVENTORY_REORDER_SERVICE = "InventoryReorderService";
}
create object of Service class and call method
EmailService emailService = SpringsUtil.getBean(SpringsUtil.EMAIL_SERVICE);

Call namedQuery inside a criteria in controller

Is possible to call namedQuery on grails inside a controller? I know that I can call a namedQuery inside another namedQuery, but i dont want to do that. Any ideas? Thanks
User.groovy
static namedQueries = {
filterUsers{
eq("age", 21)
}
}
MyController.groovy
def r = User.createCriteria().list {
eq("id", 1)
filterUsers() //not possible
}
or..
MyController.groovy
//not possible too
//Cannot invoke method createCriteria() on null object
def r = User.filterUsers().createCriteria().list {
eq("id", 1)
}
Here's an example:
Domain:
class User {
int age
String userName
static namedQueries = {
filterUsers {
eq("age", 21)
}
}
static constraints = {
}
}
Controller:
class TestController {
def index = {
def users = User.filterUsers {
and {
like 'userName', 'Derek%'
}
}
render users as JSON
}
}
Also, you can find more about this here: Reference Documentation

Resources