I have search around stack overflow and have found some similar instances of my problem but there fixes done seem to work for mine. (example of simular one:Grails - Can't call service from Controller --> always get "Cannot invoke method on null object error")
My service can be summed up like this
class AuditService {
AuditService auditService
def sql
def dataSource
static transactional = true
def pullLogs(String username, String id) {
if(username != null && id != null) {
sql = new Sql(dataSource)
println "Data source is: " + dataSource.toString()
def schema = dataSource.properties.defaultSchema
sql.query('select USERID, AUDIT_DETAILS from DEV.AUDIT_LOG T WHERE XMLEXISTS(\'\$s/*/user[id=\"' + id + '\" or username=\"'+username+'\"]\' passing T.AUDIT_DETAILS as \"s\") ORDER BY AUDIT_EVENT', []) { ResultSet rs ->
while (rs.next()) {
def auditDetails = new XmlSlurper().parseText(rs.getString('AUDIT_EVENT_DETAILS'))
println auditDetails
}
}
sql.close()
}
}
}
The way im trying to call it is likes this
UserController {
def auditService
show(Long id){
def UserInstance = User.get(id)
//Also tried def auditResults = auditServices.pullLogs(UserInstance.username, UserInstance.id)
def auditResults = auditServices(UserInstance.username, UserInstance.id)
System.out.println(" "+ auditResults)
[UserInstance: UserInstance,params:params]
}
}
The error I get is
Class:
java.lang.NullPointerException
Message:
Cannot invoke method pullLogs() on null object
Im pretty stumped. (Query was given to me)
Any Ideas/Opnions/Help is greatly appriciated!
Thanks!
In UserController you have
def auditService
But then
def auditResults = auditServices(UserInstance.username, UserInstance.id)
which should be
def auditResults = auditService.pullLogs(UserInstance.username, UserInstance.id)
As for the "FactoryBean not initialized" error, for that you can simply remove the
AuditService auditService
from inside AuditService - it isn't necessary as you can just use this if you need a reference to AuditService from within its own code.
The name of your service is LogService or AuditService? If it's AuditService your attribute name in the controller have an s that shouldn't.
class UserController {
def auditService //Name should be the same of the service, not in plural
...
}
Related
I have the two domain clases:
class Persona {
String nombre
String apellidos
static hasMany = [pertenencias: Pertenencia]
static constraints = {
}
static mapping = {
pertenencias cascade: "all-delete-orphan"
}
}
class Pertenencia {
String nombre
static belongsTo = [persona:Persona]
static constraints = {
}
}
The service:
class MembresiaService {
#Transactional
def saveAll() {
def p = new Persona(nombre: 'carlos', apellidos: 'gm')
p.addToPertenencias(nombre: 'auto')
p.addToPertenencias(nombre: 'computadora')
p.addToPertenencias(nombre: 'casa')
p.save()
}
#Transactional
def deletePertenencias() {
def p = Persona.get(1)
p.pertenencias?.clear()
}
}
And the controller:
class TestController {
def membresiaService
def index() {}
def saveAll() {
membresiaService.saveAll()
redirect(action: "index")
}
def deletePertenencias() {
membresiaService.deletePertenencias()
redirect(action: "index")
}
}
When I execute saveAll() method from controller it saves the data in the database, when I execute deletePertenencias() from controller it deletes the "pertenecias" collection of Persona from the database (as expected).
I have installed the Grails console plugin , first time I execute the lines of saveAll() service method in the console, the result is the "persona" and its "pertenencias" in database. Then I execute the lines of deletePertenencias() service method in console but it doesn't delete the data of database and the "persona" object mantains the "pertenencias" (as if I had not run deletePertenencias() code).
Anyone kwnow why the code executed from console gives unexpected results?
I expect the result was the same from controller and console but the behaviour is different.
In Grails, services are singletons by default. Can I keep it that way and still create an instance of an inner class of that service from a controller?
//by default grails makes MyTestService a singlton
class MyTestService{
public class InnerTest{
String msg;
def addMsg(String str){
this.msg=str;
}
def printMsg(){
println this.msg;
}
}
}
In controller "MyController"...
def m=myTestService.getInstance().new InnerTest();
//produces " MyTestService.InnerTest cannot be cast to MyTestService.InnerTest"
def m=myTestService.new InnerTest();
//No signature of method:MyController.InnerTest()
You should be able to do something like:
class MyTestService{
public class InnerTest{
String msg;
def addMsg(String str){
this.msg=str;
}
def printMsg(){
println this.msg;
}
}
def InnerTestFactory() {
new InnerTest()
}
}
And use it from your controller:
def m=myTestService.InnerTestFactory();
Initial Problem
If you have different methods that basically have only one line different, would there be a way to make it DRY by creating one method.
Example:
def showA( ) {
def instance
try {
instance = A.findById( params.id )
} catch ( Exception e ) {
def message = "Error while retrieving details for the given id ${ params.id }, $e"
log.error message
responseAsJson( 400, "Invalid id", message )
return false
}
return checkAndRender(instance, params.id);
}
def showB( ) {
def instance
try {
instance = B.findByBId( params.BId )
} catch ( Exception e ) {
def message = "Error while retrieving details for the given id ${ params.id }, $e"
log.error message
responseAsJson( 400, "Invalid id", message )
return false
}
return checkAndRender(instance, params.id);
}
So, would there be a way to make one method and simply pass as parameter:
The domain class
the ID to search for
Or would it be better to pass an SQL statement instead?
Update
Based on #dmahapatro comment, I came up with the following:
def showA( ) {
def clos = {id -> A.findByAId( id ) }
return findAndShow(clos, params.AId, params )
}
def showB( ) {
def clos = {id -> B.findByBId( id ) }
return findAndShow(clos, params.BId, params )
}
def findAndShow(Closure closure, def id, def p)
{
def instance
try {
instance = closure(id)
}
catch ( Exception e ) {
def message = "Error while retrieving instance details for the given id ${ id }, $e"
log.error message
responseAsJson( 400, "Invalid Id", message )
return false
}
return checkAndRender(instance, id);
}
Only remaining issues are:
How to cleanup even further / make it cleaner.
How to bypass warning:
The [findAndShow] action in [ApiController] accepts a parameter of
type [groovy.lang.Closure]. Interface types and abstract class types
are not supported as command objects. This parameter will be ignored.
def findAndShow(Closure closure, def id, def p)
First thing you should worry if you want a DRY code, is define a better exception handling. Try-catching your code everywhere to handle response to the client is not very DRY, if you put your data-access code in services, you can throw exceptions from them and use a global controller for catch the errors and handle the responses. E.g:
class ErrorController {
def serverError() {
if (request.format == 'json') {
//Code for handling errors in json request, request.exception stores the data about the exception.
} else {
//Code for handling errors in non-json request, e.g:
render(view: 'error', model: [msg: 'Something went wrong']) //add an error view for this
}
}
}
If you like, you can also add handlers for other types of errors (403, 404, etc)
Add to UrlMappings.groovy
"500"(controller: "error", action: "serverError")
Now you can refactor your code using your new error handling, and reflection:
Controller:
class MyController {
def myService
def show() {
def result = myService.myFind(params.className,params.id)
render result as JSON //Render stuff
}
}
Service:
import grails.util.Holders
class MyService {
def myFind(String className, Long id) {
def result = Holders.getGrailsApplication().getDomainClass('com.mypack.'+ className).findById(id)
if(!result) {
throw new ServiceException('really descriptive and usefull error msg')
}
}
}
I defined a ServiceException class so i can add custom logic for it in my ErrorController using the instanceOf operator.
I have a few fields in my User.groovy model class
fName
lName
userName
pwd
yourTel
The user will be entering the first 4 fields shown above. The 5th field yourTel will be hardcoded.
def create() {
[userInstance: new User(params)]
}
How can I do it ?
This is what I already tried:
def create() {
userInstance.yourTel = "2323232"
params = userInstance.yourTe
[userInstance: new User(params)]
}
SAVE
def save() {
def userInstance = new User(params)
if (!userInstance.save(flush: true)) {
render(view: "create", model: [userInstance: userInstance])
return
}else {
def userRole = Role.findOrSaveWhere(authority:'ROLE_USER')
if(!userInstance.authorities.contains(userRole)){
UserRole.create(userInstance, userRole, true)
}
redirect(controller:"login", action : "auth")
}
}
MODEL
static constraints = {
...
yourTel blank:true , nullable: false
Your approach works too with a bit of tweak:
def create() {
def instance = new User(params)
instance.yourTel="2323232"
[userInstance: instance]
}
The [userInstance: instance] the left is the key that will be used by your models, the right hand side is what you are passing to it. Here you first create the new User(params) then bind it with params and then you can tweak it and pass it back to your model.
def save() {
def userInstance = new User(params)
// set the yourTel to some value
userInstance.yourTel="2323232"
if (!userInstance.save(flush: true)) {
render(view: "create", model: [userInstance: userInstance])
params is a map, so put your data like
params.yourTel="2323232"
or
params.put("yourTel","2323232")
Now your code becomes:
def create() {
params.yourTel="2323232"
[userInstance: new User(params)]
}
The getStarted action redirects to companyInfo action which renders companyInfo.gsp and immediately after the page rendering, companyInfo action getting called one more time. I don't understand what the problem is.
class MyController {
#Secured('ROLE_USER')
def getStarted(){
def renderParams = [view: 'getStarted', model: [:]]
if(request.method != 'POST') {
render(view: 'getStarted')
} else {
def company = new Company()
.......
redirect(action: 'companyInfo', params: [id: company.id])
}
}
#Secured('ROLE_USER')
def companyInfo() {
def renderParams = [view: 'companyInfo', model: [:]]
if (request.method != 'POST') {
renderParams.model.cmpId = params?.id
render(renderParams)
}
}
}
See this answer. Grails trys to map get* to properties. And when the controller is called grails tries to map getStarted to a property called started, calling the method. So, Never Use get**** as your action name