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.
Related
I have a class in src/groovy like this
public class MyClass {
#AutoWired
SomeOtherClass someOtherClass
String test() {
return someOtherClass.testMethod()
}
}
When I write a test for this method I am getting an error: Cannot invoke method testMethod() on null object.
This is my test :-
def "test test" () {
expect:
myClass.test() == "somevalue"
}
What am I doing wrong? Is there a way to mock the #Autowired class?
You need to mock your someOtherClass. Something like this
def "test test"(){
setup:
myClass.someOtherClass = Mock(SomeOtherClass)
myClass.someOtherClass.testMethod() >> "somevalue"
expect:
myClass.test() == "somevalue"
}
Though the previous answer should work, spock provide more elegant way of injecting beans as per need. You could use doWithSpring closure to declare beans just like spring dsl support provided in grails using resources.groovy.
class MyClass extends Specification{
def setup(){
static doWithSpring={
someOtherClass(SomeOtherClass)
//declare below if want to inject myClass somewhere else as a bean else not
/*myClass(MyClass){bean->
someOtherClass = someOtherClass
}*/
}
}
def "test test" () {
expect:
myClass.test() == "somevalue"
}
}
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;
}
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"
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);
I use both Spring and Spock plugins in my current project. I wrote a bunch of test cases in Spock. All are passing without any error (All are GREEN!). But the same tests are failing if I try to test the app after some time. I don't why this happens. This is the code (one of my failing test):
def 'list action: 1 user'() {
setup:
mockDomain(User,[userInstance])
expect:
controller.list() == [userInstanceList: [userInstance] , userInstanceTotal: 1]
params.max == 10
where:
userInstance = new User(username:"antoaravinth",password:"secrets")
}
I get a big error for this:
java.lang.NullPointerException: Cannot invoke method encodePassword() on null object
at mnm.schedule.User.encodePassword(User.groovy:34)
at mnm.schedule.User.beforeInsert(User.groovy:42)
at org.grails.datastore.gorm.events.DomainEventListener.invokeEvent(DomainEventListener.java:188)
at org.grails.datastore.gorm.events.DomainEventListener.beforeInsert(DomainEventListener.java:110)
at org.grails.datastore.gorm.events.DomainEventListener.onPersistenceEvent(DomainEventListener.java:73)
at org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener.onApplicationEvent(AbstractPersistenceEventListener.java:46)
at org.grails.datastore.mapping.engine.EntityPersister.cancelInsert(EntityPersister.java:227)
at org.grails.datastore.mapping.engine.NativeEntryEntityPersister.executeInsert(NativeEntryEntityPersister.java:1321)
at org.grails.datastore.mapping.engine.NativeEntryEntityPersister$1.run(NativeEntryEntityPersister.java:698)
at org.grails.datastore.mapping.core.impl.PendingOperationExecution.executePendingOperation(PendingOperationExecution.java:33)
at org.grails.datastore.mapping.core.AbstractSession.flushPendingOperations(AbstractSession.java:322)
at org.grails.datastore.mapping.core.AbstractSession.flushPendingInserts(AbstractSession.java:314)
at org.grails.datastore.mapping.core.AbstractSession.flush(AbstractSession.java:237)
at org.grails.datastore.mapping.query.Query.flushBeforeQuery(Query.java:596)
at org.grails.datastore.mapping.query.Query.list(Query.java:562)
at org.grails.datastore.mapping.query.Query.singleResult(Query.java:606)
at org.grails.datastore.gorm.GormStaticApi.count_closure11(GormStaticApi.groovy:311)
at org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:301)
at org.grails.datastore.gorm.AbstractDatastoreApi.execute(AbstractDatastoreApi.groovy:34)
at org.grails.datastore.gorm.GormStaticApi.count(GormStaticApi.groovy:307)
at mnm.schedule.UserController.list(UserController.groovy:241)
at mnm.schedule.UserControllerSpec.list action: 1 user(UserControllerSpec.groovy:34)
The Domain class :
package mnm.schedule
import org.example.*;
class User extends SecUser {
Profile profile
String username
String password
static constraints = {
username(unique:true,size:3..15, blank:false)
password(blank:false)
String toString() {
this.username
}
static mapping = {
cache true
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
Set<SecRole> getAuthorities() {
SecUserSecRole.findAllBySecUser(this).collect { it.secRole } as Set
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
}
The same test is passing at times and at time throwing this error. Whats wrong here? How can it pass at times and fail at times?
Thanks in advance.
Check this blog as an example of mocking springSecurityService in domain objects
http://www.block-consult.com/blog/2011/08/17/inject-spring-security-service-into-domain-class-for-controller-unit-testing/ or check https://stackoverflow.com/a/9789619/206351