I've seen that others have had this problem, but I have yet to find a solution that works in my case. In my update method of my domain controller, I am trying to implement optimistic locking by checking version and using rejectValue(); however, I am clearly doing something wrong. I have verified that rejectValue() is being called, but it doesn't appear to work.
Also, the domain instance with bad data saves anyway. Any advice or help is appreciated. Here is my update method with the problem:
def update(Cohort cohortInstance) {
if (cohortInstance == null) {
notFound()
return
}
cohortInstance = Cohort.get(params.id)
if (cohortInstance == null) {
notFound()
return
}
if (params.version) {
def version = params.version.toLong()
if (cohortInstance.version > version) {
cohortInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'cohort.label', default: 'Cohort')] as Object[],
"Another user has updated this Cohort while you were editing")
render(view: "edit", model: [cohortInstance: cohortInstance])
return
}
}
// Bind browser data to this cohort Instance now that we've passed the concurrency check
cohortInstance.properties = params
if (cohortInstance.hasErrors()) {
respond cohortInstance.errors, view:'edit'
return
}
cohortInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'Cohort.label', default: 'Cohort'), cohortInstance.proposedCode])
redirect cohortInstance
}
'*'{ respond cohortInstance, [status: OK] }
}
}
In your update method definition (def update(Cohort cohortInstance) {}), you are initializing the instance of Cohort which is a new feature in grails 2.3.x, where grails automatically initializes the instance & do parameter binding for you .
Then again on line 4, you are getting the Cohort instance by using Cohort.get(params.id). So now you have two options: First is that, you can use read method instead of get and remove the auto initialization from the action signature or second is that, you can remove this line (cohortInstance = Cohort.get(params.id)) and add a Transactional annotation at update action like:
import grails.transaction.Transactional
#Transactional(readOnly = true)
def update(Cohort cohortInstance) {
...
}
Any of these work.
Related
I have a list view that can be sorted, searched and filtered. From that list view the user can edit items in multiple steps. Finally after editing and reviewing the changes the user goes back to the list. Now I want the list to use the same sorting, search term and filters that the user set before and show the correct results.
How can multiple paramters (sorting, search, filter) be stored and reused when showing the list action?
Possible unsatisfactory ways that I thought of:
pass through all the needed parameters. Does work hardly if there are multiple actions involved between the two list action calls
save the parameters in the session object. This seems to require a lot of code to handle multiple parameters (check if parameter was passed to action, store new value, if parameter was not passed, get old parameter from session, handle empty string parameters):
Long longParameter
if(params.containsKey('longParameter')) {
longParameter = params.getLong('longParameter')
session.setAttribute('longParameter', longParameter)
} else {
longParameter = session.getAttribute('longParameter') as Long
params['longParameter'] = longParameter
}
If you want to make it more generic you could use an Interceptor instead.
This could perhaps be generalized like this:
class SessionParamInterceptor {
SessionParamInterceptor() {
matchAll() // You could match only controllers that are relevant.
}
static final List<String> sessionParams = ['myParam','otherParam','coolParam']
boolean before() {
sessionParams.each {
// If the request contains param, then set it in session
if (params.containsKey(it)) {
session[it] = params[it]
} else {
// Else, get the value from session (it will be null, if not present)
params[it] = session[it]
}
}
true
}
}
The static sessionParams holds the parameters you want to store/retrieve from session.
If the params contains an element from the list, it is stored in session under the same name. If not, it is taken from session (given that it exists).
In your controller, you can now just access params.getLong('theParam') like you always would. You could also use Grails parameter conversion:
def myAction(Long theParam) {
}
Lots of LOC saved.
I use the session as well. Here is a sample that you may adapt to your needs:
def list() {
if (request.method == 'GET' && !request.queryString) {
if (session[controllerName]) {
// Recall params from memory
params.putAll(session[controllerName])
}
} else {
// Save params to memory and redirect to get clean URL
session[controllerName] = extractParams(params)
redirect(action: actionName)
return
}
// Do your actions here...
}
def extractParams(params) {
def ret = [:]
params.each { entry ->
if (entry.key.startsWith("filter_") || entry.key == "max" || entry.key == "offset" || entry.key == "sort" || entry.key == "order") {
ret[entry.key] = entry.value
}
}
return ret
}
Using session is your best bet. Just save the preference when preferred. I mean, when user sorts, or filter, just save that information in the session, for that particular <controller>.<action>, before returning the page. Next time, check the session, if it has anything related to that <controller>.<action>, apply those; otherwise render the default page.
You might like to use some Interceptor for this, as suggested by sbglasius, here.
I hope you're getting my point.
I'm trying to set the date of birth of a person using jQuery Datepicker. However, all I get is that the Property dateOfBirth must be a valid Date.
So, originally, my controller looks like this:
def update(Person personInstance) {
if (personInstance == null) {
// do Something
return
}
if (personInstance.hasErrors()) {
respond personInstance.errors, view: 'edit'
return
}
// do the rest
}
I figured out, that with jQuery I should use a SimpleDateFormat object in order to generate a proper Date object. Nevertheless, even if I directly assign a new Date object to dateOfBirth and subsequently validating the personInstance domain object - like in the following code segment - I still get the Property dateOfBirth must be a valid Date error.
def update(Person personInstance) {
if (personInstance == null) {
// do Something
return
}
// added code
personInstance.dateOfBirth = new Date()
personInstance.validate()
// added code
if (personInstance.hasErrors()) {
respond personInstance.errors, view: 'edit'
return
}
// do the rest
}
Thank you for any help :)
The reason why you are still seeing errors is because validation is automatically called after binding your command/domain object when the method is called.
Use personInstance.clearErrors() before calling personInstance.validate() manually to clear out any existing binding/validation errors. You can see more about this in the documentation.
I use the Grails Application Info Plugin to get active sessions as:
ScopesInfoService scopesInfoService
List<Map<String, Object>> activeSessionsMap = scopesInfoService.getSessionsInfo()
activeSessionsMap.each { sessionMap ->
def tmpSession = sessionMap.session
}
How can I see which page a user with a session has been requested and is requesting?
Create a filter and store the current controller and action or request.forwardURI for each request in a variable called currentLocation or something. When you access sessions just read that. you can be creative with it and store any data. but not sure this is a proper approach anyway.
storeLocation(controller: '*', action: '*') {
before = {
session.currentLocation = "$controllerName/$actionName"
}
}
This plugin will not give you that. You need to develop something yourself.
You can create a grails Filter that saves to your database (create a new table for this) the information about the session, user and about the URI (your pages) is being requested.
Sample filter:
class UserInfoFilters {
def filters = {
all(controller:'*', action:'*') {
before = {
SessionUserInfoDomainClass s = new SessionUserInfoDomainClass()
// populate your domain class above with the info you need. Examples:
s.user = session.user
s.controller = controllerName
s.save()
}
}
}
}
Then you can easily have some UI (even with scaffolg) of this SessionUserInfoDomainClass to read the info you want.
I have a domain object with following:
class Color {
String name
String fileLocation
static constraints = {
name (nullable: false, blank: false)
}
}
In my controller I'm doing the following:
def save() {
def colorInstance = new Color(params)
if (colorInstance.save(flush: true)) {
def file = request.getFile("myfile")
if (!file.empty && uploadService.isFileAllowed(file)) {
uploadService.uploadFile(file, file.originalName, "folderName")
}
}
else {
render (view: "create", model: [coorInstance: colorInstance])
}
}
This all works fine however, I'm not sure how to throw an error when the uploaded file isn't what is allowed. i.e. uploadService.isFileAllowed(file) returns false ??
How can I throw an error back to the user saying
Uploaded file isn't allowed
when uploadService.isFileAllowed(file) returns false ?
Note:
The isFileAllowed method is reading first few bytes of a file to determine what type of file it is.
What if you save an error message to flash memory and then render it on the page if it exists? See this post for help.
if (!file.empty && uploadService.isFileAllowed(file)) {
uploadService.uploadFile(file, file.originalName, "folderName")
} else {
flash.error = "Uploaded file isn't allowed"
}
apply this login in your controller
String fileName = "something.ext";
int a = fileName.lastIndexOf(".");
String extName = fileName.substring(a);
System.out.println(fileName.substring(a));
ArrayList<String> extList = new ArrayList<String>();
extList.add("jpg");
extList.add("jpeg");
extList.add("png");
if(extList.contains(extName))
{
System.out.println("proceed");
}
else{
System.out.println("throw exception");
}
So if isFileAllowed returns false or the file is empty, it will add an error to the colorInstance to the fileLocation property. It will only upload the file if the colorInstance validates successfully (to prevent files uploaded for unsaved objects).
As a side note, I prefer saving files in tables partly for this reason. It makes validation much less clunky and its impossible to have a disconnect between your objects and the files. - Just my 2c.
def save() {
def colorInstance = new Color(params)
def file = request.getFile("myfile")
if (!file.empty && uploadService.isFileAllowed(file)) {
if (colorInstance.validate()) {
uploadService.uploadFile(file, file.originalName, "folderName")
}
}
else {
colorInstance.errors.rejectValue('fileLocation','error.message.code.here')
}
if (colorInstance.save(flush: true)) {
//do whatever here
}
else {
render (view: "create", model: [coorInstance: colorInstance])
}
}
I am trying to use beforeInsert in my user domain class.
class User {
String reEnterPassword
static constraints = {
password(blank: false, nullable: false, size:5..50, validator: {password, obj ->
def reEnterPassword = obj.properties['reEnterPassword']
if(reEnterPassword == null) return true
reEnterPassword == password ? true : ['invalid.matchingpasswords']
})
reEnterPassword(bindable:true, blank: false);
}
def beforeInsert = {
password = password.encodeAsSHA()
}
String toString(){
name
}
static transients = ['reEnterPassword']
}
in my controller i have save method ( generated)
def save() {
def userInstance = new User(params)
if (!`userInstance.save(flush: true)`) {
render(view: "create", model: [userInstance: userInstance])
return
}
This is throwing exception
Grails runtime exception, org.hibernate.AssertionFailure: null id in entry (don't flush the Session after an exception occurs), when domain objects save method encounters a SQL Exception
I read in the documentation for auto timestamping that
Do not attempt to flush the session within an event (such as with obj.save(flush:true)). Since events are fired during flushing this will cause a StackOverflowError.
In this case how to save my userInstance.save(flush: true) I tried to remove flush:true but still i am getting same error. if i remove flus:true..then when i need to call. When hibenate will flush all these records.
I tried the solution defined this JIRA ticket
Please help me out. Thank you
Can it be that you have other validation errors?
If you put your code in the beforeValidate method it will work:
def beforeValidate = {
password = password.encodeAsSHA()
}
I guess I'm too late to help you, but I hope it helps others with the same issue.
Greetings, Urs
Change your
def beforeInsert = {
password = password.encodeAsSHA()
}
to
def beforeInsert() {
password = password.encodeAsSHA()
}
and that should do the trick
I believe if the beforeInsert method return false then you get the "null id in entry" exception. Perhaps this is treated as an indication that validation has failed.
e.g. the following will cause the exception
def beforeInsert() {
flag = false
}
however the following should work OK
def beforeInsert() {
flag = false
return true
}