I came accross an interesting problem in my code today.
I am using Grails 2.2.0.
Here is the code
def user = lookupUserClass().get(params.id)
log.info "[update]user.subscriptionExpiryDate1: " + user.subscriptionExpiryDate
user.subscriptionExpiryDate = Calendar.getInstance();
log.info "[update]user.subscriptionExpiryDate2: " + user.subscriptionExpiryDate
if (user.subscriptionExpiryDate instanceof Calendar ) {
log.error "***** Is A Calendar Instance ***"
} else if (user.subscriptionExpiryDate instanceof String ) {
log.error "***** Is A String Instance ***"
} else {
log.error "***** Is Something else ***"
}
if (!user.save()) {
log.error "[update]Error occured saving user. Errors are: "
user.errors.each { err -> log.error err; }
render view: 'edit', model: buildUserModel(user)
return
} else {
log.info "[update]Successfully saved user"
}
subscriptionExpiryDate is a calendar property in my User object.
When I perform the save I get the following error
Failed to convert property value of type 'java.lang.String' to required type 'java.util.Calendar' for property 'subscriptionExpiryDate'; nested exception is java.lang.IllegalArgumentException: Could not parse date: Unparseable date: 05/03/2013
Could anyone please explain why I would be seeing this error for the above code as nothing is standing out
This question has been resolved as per Andrew's suggestion:
Are you doing any data binding on the user object before the code you pasted? If you are binding to the subscriptionExpiryDate property, you will need a PropertyEditor to do the String -> Calendar conversion
Related
I am very new to Groovy and this is an old application where the author is no longer with our organization. None of the previous questions that look similar offered any help. The application needs to send a simple message to the user to warn they are missing an entry before they con continue on.
I have made no fewer than 20 changes from flash.message to confirm. Flash causes the application to jump all the way to the user login function. This confirm is giving a crash message: Error 500: Executing action [submitrequest] of controller [SdrmController] caused exception: Runtime error executing action
def submitrequest = {
def testChecker
testChecker = [params.fullExpName].flatten().findAll { it != null }
log.info('testChecker.size = ' + testChecker.size)
if (testChecker.size > 0) {
if (!confirm('Submitting can not be undone, are you sure?')) return
} else {
if (!confirm('You have to pick an expedition. Please return to your Request and pick at least one expedition.')) return
} else {
return
}
}
// rest of long time working code here
}
Expected Result is a simple message to screen tell the user to pick an "Expedition" from a list and then the code returns to the same point so the user can make the change then hit the submit again.
Then full message:
No signature of method: SdrmController.confirm() is applicable for argument types: (java.lang.String) values: [You have to pick an expedition. Please return to your Request and pick at least one expedition.] Possible solutions: notify(), render(java.lang.String)
-- flash.message worked for our situation.
`legChecker = [params.programLeg].flatten().findAll{it!=null}
if(requestInstance.futurePast == "future" && expChecker.size<1) {
flash.message = " you must select a future expedition "
render(view: 'stepstart', model: [....])
return
}`
I created a custom attribute, inheriting from HandleErrorAttribute:
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
try
{
Utility.LogAndNotifyOfError(filterContext.Exception, null, true);
}
catch(Exception ex)
{
filterContext.Exception = ex;
}
}
}
, and then registered with:
filters.Add(new CustomHandleErrorAttribute());
This has always worked as intended. However a common problem with my log method is that it uses a custom event log source when writing to the event log, which the app pool account typically doesn't have the permissions to create. Creating the event log source is a simple powershell script, however I wanted to actually include that tidbit in the error:
try
{
log.WriteEntry(error, EventLogEntryType.Error);
}
catch(SecurityException ex1)
{
throw new ErrorHandlerException($"The event log could not be written to due to a SecurityExcption. The likely issue is that the '{eventLogSource}' does not already exist. Please run the following powershell command:\r\n"
+ $"New - EventLog - LogName Application - Source {eventLogSource}", ex1);
}
The problem is that the catch in the OnException is never hit. When debugging, the custom error I throw from LogAndNotifyOfError instead triggers a second call to OnException, and the detail of my ErrorHandlerException is never seen. I want the asp.net error page that comes up to be with my custom error detail rather than the SecurityException that was originally raised.
You can even see the surrounding try in the displayed error:
Edit: Entire log method listed:
public static void LogAndNotifyOfError(Exception ex, String extraInfo, Boolean sendEmail)
{
//if the error handler itself faulted...
if (ex is ErrorHandlerException)
return;
string eventLogName = "Application";
string eventLogSource = "MySourceName";
String error = ex.ToString();
if (error.Length > 28000)
error.Substring(0, 28000);//event log is limited to 32k
error += "\r\n\r\nAdditional Information: \r\n"
+ "Machine Name: " + Environment.MachineName + "\r\n"
+ "Logged in user:" + App.CurrentSecurityContext.CurrentUser?.UserId + "\r\n"
+ extraInfo + "\r\n";
EventLog log = new EventLog(eventLogName);
log.Source = eventLogSource;
try
{
log.WriteEntry(error, EventLogEntryType.Error);
}
catch(SecurityException ex1)
{//this doesn't work - for some reason, OnError still reports the original error.
throw new ErrorHandlerException($"The event log could not be written to due to a SecurityExcption. The likely issue is that the '{eventLogSource}' does not already exist. Please run the following powershell command:\r\n"
+ $"New - EventLog - LogName Application - Source {eventLogSource}", ex1);
}
//if the email-to field has been set...
if (!String.IsNullOrEmpty(App.Config.General.ErrorHandlerSendToAddresses) && sendEmail)
{
//...then send the email
MailMessage email = new MailMessage();
email.To.Add(App.Config.General.ErrorHandlerSendToAddresses);
email.IsBodyHtml = false;
email.Subject = String.Format("Error in {0}", eventLogSource);
email.Body = email.Subject + "\r\n\r\n"
//+ "Note: This error may be occuring continuously, but this email is only sent once per hour, per url, in order to avoid filling your mailbox. Please check the event log for reoccurances and variations of this error.\r\n\r\n"
+ "The error description is as follows: \r\n\r\n"
+ error + "\r\n\r\n";
SmtpClient smtp = new SmtpClient();
smtp.Send(email);
}
}
I figured it out (sort of). It would appear that when the newly throw exception has an inner exception, it is only displaying that inner exception. It does not matter what the type is on the outer or inner exception.
I'm trying to do some serverside validation in grails and pass my errors back to the frontend as json to be processed by angularjs.
Error conditions
Department - required
Department - unique
Description - foobar not allowed
I have the following code.
Controller
def saveDepartment() {
def errors = []
def success = true
def department
try{
department = departmentService.save(request.JSON);
if(department.hasErrors()) {
success = false
errors = department.errors.fieldErrors;
}
} catch(Exception e){
e.printStackTrace()
errors = "Unknown"
success = false
if(log.errorEnabled){
log.error("save department encountered unknown error: ", e)
}
response.status = 500
} finally {
respond ([success:success, errors:errors, department:department]) as JSON;
}
}
Service
def save(jsonObj) {
def dept = new Department();
dept.setName(jsonObj.name);
dept.setDescription(jsonObj.description);
if(dept.description.equals('foobar')) {
dept.errors.rejectValue('description', 'foobar', 'Foobar is not allowed')
}
if (!dept.save()) {
dept.discard();
}
return dept;
}
Service Method Attempt 2 with debugging code
def save(jsonObj) {
def dept = new Department();
dept.setName(jsonObj.name);
dept.setDescription(jsonObj.description);
if(dept.description.equals('foobar')) {
println 'rejected value '
dept.errors.rejectValue('description', 'foobar', 'Foobar is not allowed')
}
println 'dept errors ' + dept.errors.allErrors.size();
if (dept.errors.hasErrors()) {
dept.errors.allErrors.each {FieldError error ->
println error
}
}
if (!dept.save(true)) {
println 'dept errors 2 ' + dept.errors.allErrors.size();
if (dept.errors.hasErrors()) {
dept.errors.allErrors.each {FieldError error ->
println error
}
}
}
return dept;
}
Output
..................rejected value
dept errors 1
Field error in object 'org.hri.leaverequest.Department' on field 'description': rejected value [foobar]; codes [foobar.org.hri.leaverequest.Department.descripti
on,foobar.description,foobar.java.lang.String,foobar]; arguments []; default message [Foobar is not allowed]
dept errors 2 1
Field error in object 'org.hri.leaverequest.Department' on field 'name': rejected value [null]; codes [org.hri.leaverequest.Department.name.nullable.error.org.h
ri.leaverequest.Department.name,org.hri.leaverequest.Department.name.nullable.error.name,org.hri.leaverequest.Department.name.nullable.error.java.lang.String,or
g.hri.leaverequest.Department.name.nullable.error,department.name.nullable.error.org.hri.leaverequest.Department.name,department.name.nullable.error.name,depart
ment.name.nullable.error.java.lang.String,department.name.nullable.error,org.hri.leaverequest.Department.name.nullable.org.hri.leaverequest.Department.name,org.
hri.leaverequest.Department.name.nullable.name,org.hri.leaverequest.Department.name.nullable.java.lang.String,org.hri.leaverequest.Department.name.nullable,depa
rtment.name.nullable.org.hri.leaverequest.Department.name,department.name.nullable.name,department.name.nullable.java.lang.String,department.name.nullable,nulla
ble.org.hri.leaverequest.Department.name,nullable.name,nullable.java.lang.String,nullable]; arguments [name,class org.hri.leaverequest.Department]; default mess
age [Property [{0}] of class [{1}] cannot be null]
Issues
If department is null and description has foobar with rejectValue, only one error, "department null" is returned, foobar does not appear in the errors.
If department contains existing value and description contains foobar, the unique constraint is returned but foobar does not appear in the errors.
If department has a good value and foobar still exist, the rejectValue doesn't prevent the save from happening and no errors are thrown. Now if I output dept.errors after the rejectValue, I can see the error actually exist.
Goal
My goal is to return all my errors and not save to the db if an error exist, what am I missing to achieve that goal?
You can do it this way:
dept.validate()
if(dept.description.equals('foobar')) {
dept.errors.rejectValue('description', 'foobar', 'Foobar is not allowed')
}
if(!dept.errors.hasErrors()) {
dept.save()
}
return dept
surely that is the validation constraints that you need to get right in the domain class or relevant validator?.
You have a set of criterias and the validation on the backend should fail to match what you expect to return or not as an error ? or maybe I am missing something
class Example {
Department deparment
static constraints = {
department(nullable:false, blank:false, unique:true, validator: checkDept)
}
static def checkDept= { val, obj, errors ->
//department has a value
if (val) {
//val is now also the same as object.department
if (obj.deparment.description='foo') {
errors.rejectValue(propertyName, "nullable.input", [''] as Object[], 'this is description as foo being rejected')
} else if (obj.deparment.name='bar') {
errors.rejectValue('department.name', "nullable.input", [''] as Object[], 'this is name of bar')
} else {
errors.rejectValue(propertyName, "nullable.input", [''] as Object[], 'this is null and being rejected')
}
}
}
}
propertyName will bind to actual object name - if you are failing based on department.something and the field names are that on the gsp page then you may need to tweak that.
The logics of above is not exactly what you have asked for but it should give you an idea of how you can customise to exactly what you wish to fail. If it doesn't match those then it will just go through as you require
When I run this code:
package nexmo
class SmsControllerController {
// Inject the service
def nexmoService
def index() {
def smsResult
def callResult
try {
// Send the message "What's up?" to 1-500-123-4567
smsResult = nexmoService.sendSms("61451062006", "What's up?")
// Call the number and tell them a message
callResult = nexmoService.call("61451062006", "Have a great day! Goodbye.")
catch (NexmoException e ) {
// Handle error if failure
}
}
}
}
I get the following error message:
Error Compilation error: startup failed:
C:\nexmo-master\grails-app\controllers\nexmo\SmsController.groovy: 19: unexpected token: catch # line 19, column 13.
catch (NexmoException e ) {
^
How do I resolve this issue?
Try closing the try statement before you the catch statement.
try {
// Send the message "What's up?" to 1-500-123-4567
smsResult = nexmoService.sendSms("61451062006", "What's up?")
// Call the number and tell them a message
callResult = nexmoService.call("61451062006", "Have a great day! Goodbye.")
}
catch (NexmoException e ) {
// Handle error if failure
}
Our authentication domain object, Operator, as boolean enabled on it.
When we set this to false, SpringSecurity + grails magically fails login attempts with the message "Sorry, your account is disabled".
We want to log such attempts. Presumably, there is some kind of listener or handler, which DOES NOT require us to implement the logic (I.e. grails just informs us that the user was rejected, not that we now have to decide if the user should be rejected or not).
We already log failed password checks, and if they fail because they had too many login attempts - we don't want it to catch those events, just the rejection due to enabled == false.
Yes, you can achieve it easily. First modify your Config.groovy so that it can redirect to a particular action on login failure:
grails.plugin.springsecurity.failureHandler.defaultFailureUrl = "/login/authfail"
// For AJAX based authentication
grails.plugin.springsecurity.failureHandler.ajaxAuthFailUrl = "/login/authfail"
Now define a LoginController with action authfail as following:
def authfail() {
String msg = ""
Exception exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
// print the kind of message you want either due to account locked, enabled = false, password expired = true and others
log.debug "User login failed due to [${exception?.message}]"
if (exception) {
if (exception instanceof AccountExpiredException) {
msg = g.message(code: "springSecurity.errors.login.expired")
} else if (exception instanceof CredentialsExpiredException) {
msg = g.message(code: "springSecurity.errors.login.passwordExpired")
} else if (exception instanceof DisabledException) {
msg = g.message(code: "springSecurity.errors.login.disabled")
} else if (exception instanceof LockedException) {
msg = g.message(code: "springSecurity.errors.login.locked")
} else {
msg = g.message(code: "springSecurity.errors.login.fail")
}
}
// redirect or respond acccordingly
}
Found a solution Solution after RTFM
MySecurityEventListener.groovy:
import org.springframework.context.ApplicationListener
import org.springframework.security.authentication.event. AuthenticationFailureDisabledEvent
class MySecurityEventListener
implements ApplicationListener<AuthenticationFailureDisabledEvent> {
void onApplicationEvent(AuthenticationFailureDisabledEvent event) {
println "DISABLED LOGGON!!!!"
}
}
Config.groovy:
grails.plugin.springsecurity.useSecurityEventListener = true
spring/resource.groovy
import com.nektan.me.bla.MySecurityEventListener
beans = {
mySecurityEventListener(MySecurityEventListener)
}
This prints the message.
However, it is not possible to access the grails services from inside the event listener, the injection does not work. We have a grails service which logs security events to the DB, which we haven't found a way to use from inside this callback.