I have a project using Struts2 on the server side and I am trying to make it work with jqGrid (using JSON format). I have several tables made with jqGrid and I am using the add/edit/delete buttons from navGrid.
The main problem I have is with server validation error messages. I have created custom validators and they work with jsp pages, using s:fielderror, but I don't know how to make them work for add/edit popups from jqGrid. I am aware that jqGrid provides the users with custom validation on client, but this has its limitations(think about testing whether the email of a user is unique, you definitely must use the database for that, or if some fields depend on each other and must be tested together, like if isManager is true, then the managerCode must be not empty and vice versa...).
When I use the client validation, there is a message in the add/edit window whenever an error occurs. Can I somehow display my server validation error messages in the window in the same way?
I managed to solve the issue. I will explain how using a simple custom validator for age field, which must be > 18 for an Employee. It is supposed next that the validator was already declared in validators.xml and mapped on the action and that the message in case of ValidationException is "An employee should be older than 18.".
Using Firebug, I figured out that the id of the error area in the form is FormError. It is possible to configure a callback function errorTextFormat in jqgrid, in order to get a response from the server and process it. In the jqgrid configuration, one could write
errorTextFormat : errorFormat,
with
var errorFormat = function(response) {
var text = response.responseText;
$('#FormError').text(text); //sets the text in the error area to the validation //message from the server
return text;
};
The problem is now that the server will send implicitly a response containing the whole exception stack trace. To deal with it, I decided to create a new result type.
public class MyResult implements Result {
/**
*
*/
private static final long serialVersionUID = -6814596446076941639L;
private int errorCode = 500;
public void execute(ActionInvocation invocation) throws Exception {
ActionContext actionContext = invocation.getInvocationContext();
HttpServletResponse response = (HttpServletResponse) actionContext
.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse");
Exception exception = (Exception) actionContext
.getValueStack().findValue("exception");
response.setStatus(getErrorCode());
try {
PrintWriter out = response.getWriter();
out.print(exception.getMessage());
} catch (IOException e) {
throw e;
}
}
/**
* #return the errorCode
*/
public int getErrorCode() {
return errorCode;
}
/**
* #param errorCode the errorCode to set
*/
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
}
It must also be configured in struts.xml as follows:
<package name="default" abstract="true" extends="struts-default">
...
<result-types>
<result-type name="validationError"
class="exercises.ex5.result.MyResult">
</result-type>
</result-types>
...
<action name="myaction">
...
<result name="validationException" type="validationError"></result>
<exception-mapping result="validationException"
exception="java.lang.Exception"></exception-mapping>
</action>
...
</package>
These are the steps I followed to get a validation error message in the add/edit window and now it works.
Related
I am trying to do form validation in Spring Web Flow. For this I am using a validator class, which is named after the model. Just like it is stated in the documentation.
The validator gets instantiated as a bean but is never called during validation. Any pointers on that issue?
flow config
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.4.xsd">
<view-state id="createTotpKeyView" view="/templates/totp/create/create" model="key">
<on-entry>
<evaluate expression="createTotpKeyAction"/>
</on-entry>
<transition on="submit" to="successfullyCreated" bind="true" validate="true"/>
</view-state>
<end-state id="successfullyCreated" view="/templates/totp/create/success"/>
</flow>
This is the action that is called in the view-state.
createTotpKeyAction
#Component
public class CreateTotpKeyAction implements Action
{
String uid = "random";
#Override
public Event execute(RequestContext context) throws Exception
{
try
{
// Create a TOTP key and put it in the view scope
TOTPKey totpKey = client.createTotpKeyForUid(uid, null);
context.getViewScope().put("key", totpKey);
return new Event(this, "success");
}
catch (Exception e)
{
log.error("Error while creating TOTP key for user: " + uid + ".\n" + e.getMessage());
// Put response message in flash scope to show it once
context.getFlashScope().put("fetchingError", true);
return new Event(this, "error");
}
}
}
This is the validator I am trying to use. EDIT renamed to match documentation.
KeyValidator
#Component
public class KeyValidator
{
[...]
public void validateCreateTotpKeyView(TOTPKey key, ValidationContext context)
{
System.out.println("VALIDATE VIEW STATE");
}
public void validate(TOTPKey key, ValidationContext context)
{
System.out.println("DEFAULT VALIDATE");
}
}
I also tried different naming schemes such as TOTPKeyValidator or TotpKeyValidator. None of them worked.
The only thing that is working, is creating a validation method in the TOTPKey class, but I don't want to use that approach.
In addition this is the log file produced during the attempted validation
Log
Mapping request with URI '/totp/create' to flow with id 'totp/create'
Resuming flow execution with key 'e5s1
Locking conversation 5
Getting flow execution with key 'e5s1'
Getting FlowDefinition with id 'totp/create'
Resuming in org.springframework.webflow.mvc.servlet.MvcExternalContext#2b551393
Restoring [FlowVariable#3b66a2de name = 'key', valueFactory = [BeanFactoryVariableValueFactory#2fbc89 type = TOTPKey]]
Processing user event 'submit'
Resolved model twofa.core.domain.TOTPKey#505439d0
Binding to model
Adding default mapping for parameter 'execution'
Adding default mapping for parameter 'totpKeyId'
Adding default mapping for parameter 'token'
Adding empty value mapping for parameter 'eventId_submit'
Validating model
Event 'submit' returned from view [ServletMvcView#19f8532f view = org.springframework.web.servlet.view.velocity.VelocityLayoutView: name '/templates/totp/create/create'; URL [/templates/totp/create/create.vm]]
Executing [Transition#2feb5361 on = submit, to = successfullyCreated]
Exiting state 'createTotpKeyView'
Entering state 'successfullyCreated' of flow 'totp/create'
Executing org.springframework.webflow.action.ViewFactoryActionAdapter#423fa131
Rendering MVC [org.springframework.web.servlet.view.velocity.VelocityLayoutView: name '/templates/totp/create/success'; URL [/templates/totp/create/success.vm]] with model map [{currentUser=null, flashScope=map[[empty]], flowRequestContext=[RequestControlContextImpl#70144045 externalContext = org.springframework.webflow.mvc.servlet.MvcExternalContext#2b551393, currentEvent = submit, requestScope = map[[empty]], attributes = map[[empty]], messageContext = [DefaultMessageContext#149807b4 sourceMessages = map[[null] -> list[[empty]]]], flowExecution = [FlowExecutionImpl#1c4b2c3e flow = 'totp/create', flowSessions = list[[FlowSessionImpl#6eea5d26 flow = 'totp/create', state = 'successfullyCreated', scope = map['key' -> twofa.core.domain.TOTPKey#73f32d0a]]]]], flowExecutionKey=e5s1, flowExecutionUrl=/totp/create?execution=e5s1, key=twofa.core.domain.TOTPKey#73f32d0a}]
Finished executing org.springframework.webflow.action.ViewFactoryActionAdapter#423fa131; result = success
Completed transition execution. As a result, the flow execution has ended
Removing flow execution '[Ended execution of 'totp/create']' from repository
Ending conversation 5
Unlocking conversation 5
It says Validating Model but nothing happens...
It came down to a wrong import statement in my validator class.
Using org.relaxng.datatype.ValidationContext instead of org.springframework.binding.validation.ValidationContext will not work.
I am developing a MVC application which has handles authorization and login information at a base controller class overriding OnActionExecuting event.
At AJAX calls when an exception arises I can handle this via attributes and display error messages with on custom model window.
My custom attribute is as follows :
public class JsonExceptionFilterAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.ExceptionHandled = true;
string msg = HttpUtility.HtmlDecode(filterContext.Exception.Message);
if (filterContext.Exception.GetType() == Type.GetType("System.UnauthorizedAccessException"))
{
msg = "Unauthorized access";
}
filterContext.Result = new JsonResult
{
Data = new
{
errorMessage = msg
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
}
But when an Exception occurs while using DataTables.Net the only mesage I get is the DataTables.NET's own error mesaages saying "... please see http://datatables.net/tn/7"
But I want to display my own Exception message as I do in other AJAX calls. Basically I want to display the error message I provide in my custom attribute response.
I have Googled it, advising to use "fnServerData" but I can not find my Exception message in any of the parameters (sSource, aoData, fnCallback, oSettings) I get by this event handler.
How can I possibly get the Exception message that my base controller returns and display it?
Regards.
P.S. : Handling the Exception in the Action and returning it does not apply here, because I do not fall to Action method at all.
consider this:
user clicks on a link
request goes to DisplayLoginAction
Login.jsp is displayed
user enters his credentials
form is mapped to ValidateLoginAction
validation fails in ValidateLoginAction
ValidateLoginAction stores the errors and returns "input"
redirecting to DisplayLoginAction..
DisplayLoginAction retrieves the errors from the interceptor and shows them to the user above the login form
user refreshes the page
The errors are gone
How should I save the errors on page refresh?
<action name="displayLoginPage" class="DisplayLoginAction">
<interceptor-ref name="store">
<param name="operationMode">RETRIEVE</param>
</interceptor-ref>
<interceptor-ref name="customStack"/>
<result name="success">Login.jsp</result>
<result name="input">Login.jsp</result>
</action>
<action name="validateloginForm" class="ValidateLoginAction">
<interceptor-ref name="store">
<param name="operationMode">STORE</param>
</interceptor-ref>
<interceptor-ref name="customStack"/>
<result name="input" type="redirectAction">displayLoginPage</result>
<result name="success">LoginConfirmation.jsp</result>
</action>
user refreshes the page
The errors are gone
How should I save the errors on page refresh?
That is the way MessageStoreInterceptor works. It is actually a feature, not a bug.
Page refresh is an action triggered by the user, that means it can be assumed he has already read the result of the previous operation (the login attempt), unless he is pressing F5 with the eyes closed.
You should WANT a message to expire after the first read.
Consider a page with a lot of non-ajax operations, like combobox depending on others, etc... If the error message is persistent, it would popup after each submit operation that does not involve going to another page.
You don't want that. You should want to get the message saying that one operation has gone wrong (or right) just after that operation. If the user then proceed with other operations, like a refresh, if those operations aren't going in error (or in a specific success state), no message should be shown.
After that, there's also the problem of when deleting from session a persistent message, because otherwise you would get that message in the next action with a RETRIEVE or AUTOMATIC operationMode. Then you could (but shouldn't) customize the MessageStore Interceptor, or writing / reading / deleting messages from and to the session on your own, basically reinventing the wheel. Or even put two MessageStore Interceptors, one RETRIEVE and one STORE for displayLoginPage Action, getting the pitfalls just mentioned.
You are also using the PRG pattern (Post/Redirect/Get), to avoid re-sending the data when refreshing the page. At the same way, you should avoid re-reading the same messages twice.
To see how this specifically works, you can take a look at the MessageStore Interceptor source code, that is quite simple:
Before Invocation, if Action is ValidationAware and operationMode is RETRIEVE or AUTOMATIC:
read actionMessages, actionErrors, fieldErrors from session;
merge them with current action's actionMessages, actionErrors, fieldErrors;
remove actionMessages, actionErrors, fieldErrors from session.
/**
* Handle the retrieving of field errors / action messages / field errors, which is
* done before action invocation, and the <code>operationMode</code> is 'RETRIEVE'.
*
* #param invocation
* #throws Exception
*/
protected void before(ActionInvocation invocation) throws Exception {
String reqOperationMode = getRequestOperationMode(invocation);
if (RETRIEVE_MODE.equalsIgnoreCase(reqOperationMode) ||
RETRIEVE_MODE.equalsIgnoreCase(operationMode) ||
AUTOMATIC_MODE.equalsIgnoreCase(operationMode)) {
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
// retrieve error / message from session
Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);
if (session == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Session is not open, no errors / messages could be retrieve for action ["+action+"]");
}
return;
}
ValidationAware validationAwareAction = (ValidationAware) action;
if (LOG.isDebugEnabled()) {
LOG.debug("retrieve error / message from session to populate into action ["+action+"]");
}
Collection actionErrors = (Collection) session.get(actionErrorsSessionKey);
Collection actionMessages = (Collection) session.get(actionMessagesSessionKey);
Map fieldErrors = (Map) session.get(fieldErrorsSessionKey);
if (actionErrors != null && actionErrors.size() > 0) {
Collection mergedActionErrors = mergeCollection(validationAwareAction.getActionErrors(), actionErrors);
validationAwareAction.setActionErrors(mergedActionErrors);
}
if (actionMessages != null && actionMessages.size() > 0) {
Collection mergedActionMessages = mergeCollection(validationAwareAction.getActionMessages(), actionMessages);
validationAwareAction.setActionMessages(mergedActionMessages);
}
if (fieldErrors != null && fieldErrors.size() > 0) {
Map mergedFieldErrors = mergeMap(validationAwareAction.getFieldErrors(), fieldErrors);
validationAwareAction.setFieldErrors(mergedFieldErrors);
}
session.remove(actionErrorsSessionKey);
session.remove(actionMessagesSessionKey);
session.remove(fieldErrorsSessionKey);
}
}
}
After Invocation, if Action is ValidationAware and operationMode is STORE or (operationMode is AUTOMATIC and Result is of type redirectAction):
read actionMessages, actionErrors, fieldErrors from action;
store actionMessages, actionErrors, fieldErrors in the session.
/**
* Handle the storing of field errors / action messages / field errors, which is
* done after action invocation, and the <code>operationMode</code> is in 'STORE'.
*
* #param invocation
* #param result
* #throws Exception
*/
protected void after(ActionInvocation invocation, String result) throws Exception {
String reqOperationMode = getRequestOperationMode(invocation);
boolean isRedirect = invocation.getResult() instanceof ServletRedirectResult;
if (STORE_MODE.equalsIgnoreCase(reqOperationMode) ||
STORE_MODE.equalsIgnoreCase(operationMode) ||
(AUTOMATIC_MODE.equalsIgnoreCase(operationMode) && isRedirect)) {
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
// store error / messages into session
Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);
if (session == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Could not store action ["+action+"] error/messages into session, because session hasn't been opened yet.");
}
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("store action ["+action+"] error/messages into session ");
}
ValidationAware validationAwareAction = (ValidationAware) action;
session.put(actionErrorsSessionKey, validationAwareAction.getActionErrors());
session.put(actionMessagesSessionKey, validationAwareAction.getActionMessages());
session.put(fieldErrorsSessionKey, validationAwareAction.getFieldErrors());
}
else if(LOG.isDebugEnabled()) {
LOG.debug("Action ["+action+"] is not ValidationAware, no message / error that are storeable");
}
}
}
Note 1: The login operation is also self-explanatory: if after logging in you land on the login page again, it can only mean that the login failed... BTW the above explanation applies to every page/functionality
Note 2: There are sites producing messages that expire automatically after X seconds, not caring about the user having read them or not... and that is against the usability, IMHO.
This question already has answers here:
Getting warning from JSF: The response was already committed by the time we tried to set the outgoing cookie for the flash
(4 answers)
Closed 5 years ago.
I meet a strange behavior with JSF 2.4 on Mojarra.
I'm using flash parameters to pass from a page to another.
Each time i arrive on a new page, i retrieve my flash parameters in Postconstruct annoted method.
Then if the page is refreshed, the user is redirect to another page. (because the flash parameters are erased after refresh).
FOr the same code , i meet this error i if i populated my selectItems from different data (hard coded or database query) :
JSF1095: The response was already committed by the time we tried to set the outgoing cookie for the flash. Any values stored to the flash will not be available on the next request.
I d'like to get ride of this, maybe something to do with :
facesContext.responseComplete();
facesContext.renderResponse()
I don't understand how to use them.
I read about 2 :
<h:selectOneMenu id ="loc" value="#{participantController.localisation}"
validatorMessage="Vous devez renseigner la localisation." >
<f:selectItems value="#{participantController.locFeaturesList}"
var="locFeature" itemLabel="#{locFeature.locName}"
itemValue="#{locFeature.locName}" />
</h:selectOneMenu>
My list object :
public static class LocFeatures{
public String locName;
public String codeName;
public LocFeatures(String locName, String codeName){
this.locName = locName;
this.codeName = codeName;
}
public String getLocName(){
return locName;
}
public String getCodeName(){
return codeName;
}
}
Put data in my list object :
public LocFeatures[] loadLocalisationpAdicapCodeAssociativeMenu() {
List <Static> loc2Code = staticBo.get(STATIC_CATEGORY_PARTICIPANT_LOCALISATION_CODE);
locFeaturesList = new LocFeatures[loc2Code.size()+1];
// if I populate my list with only some values no errors will be thrown but it doesn't work when i populate it by a big database request
//locFeaturesList = new LocFeatures[1];
locFeaturesList[0] = new LocFeatures("-----",null); // Associations Classe - Name
int indice = 1;
for (Static assocation : loc2Code) {
locFeaturesList[indice] = new LocFeatures(assocation.getKey(),assocation.getValue()); // Associations Classe - Name
indice ++;
}
return locFeaturesList;
}
My #PostConstruct method :
#PostConstruct
public void setFlashParam() {
//locFeaturesList = loadLocalisationpAdicapCodeAssociativeMenu();
FacesContext facesContext = FacesContext.getCurrentInstance();
String studyNameFlashed = (String) facesContext.getExternalContext().getFlash().get("study_name");
// Gere un refresh qui ferait disparaitre le type de l'étude.
if (studyNameFlashed == null) {
try { ExternalContext ec = facesContext.getExternalContext(); ec.redirect(ec.getRequestContextPath() + "/Accueil"); } catch (IOException e) {e.printStackTrace();}
return;
}
return;
}
Try always to go with Mojarra latest versions to work with flash. In your case, try to update it to 2.2.6. As stated in several posts around here, there have been a lot of issues with flash scope in Mojarra implementations. However, it seems they've fixed the problem. See this answer (which BTW explains how to use the context properly).
See also:
JSF/Mojarra "flash scope" problems
Exception about flash in Mojarra JSF
Hi all I have a problem when working with struts validation as the following :
an edit profile page loaded , when user click save a validation should be executed if an error exist a redirect to the same page must happen with validation error showed, and data loaded, else execute method called. what happen that a errors list has values but no errors showed up in the screen, a snap of my code is :
I am using struts 1
<action path="/selectUserAction" validate="true" input="/selectUserAction.do" type="com.bg.sharjah.usermanagement.manageusersgroups.web.actions.SelectUserAction"
name="editUserProfileFormBean" scope="request">
#Override
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
//Logger.getAnonymousLogger().log(Level.WARNING,"");
ActionErrors errors = new ActionErrors();
try{
errors = super.validate(mapping, request);
System.out.println("validate <<<<<<<<<<< "+errors.size());
}catch(Exception e)
{
e.printStackTrace();
}
return errors;
// Validation.match(errors, password, confirmPassword);
//Validation.match(errors, email, confirmEmail);
}
Note:
my form bean is used with different actions, also I need to know how to redirect to the same page using input attribute ?
Thanx all I find out the problem it's a resource bundle issue.