Mark inputText as invalid in Invoke Applications phase - validation

I am performing some business rule validations in the Invoke Applications phase, and when there is an error, a custom Exception will be thrown. The custom exception is handled in a custom JSF ErrorHandler, where the input component in question will be marked as invalid, FacesMessages created and validation will be failed on the FacesContext.
Bean
public void performAction() {
if ("aaa".equals(input)) {
// custom exception: arg1 - Error Message, arg2 - clientId
throw new ServiceValidationException("Something went wrong", ":f:input");
}
}
XHTML
<h:form id="f">
<p:inputText id="input" value="#{bean.input}" />
<h:commandButton value="Submit" action="#{bean.performAction}"/>
</h:form>
Custom JSF ErrorHandler
#Override
public void handle() throws FacesException {
try {
Iterator<ExceptionQueuedEvent> unhandledExceptionQueuedEvents = getUnhandledExceptionQueuedEvents().iterator();
if (unhandledExceptionQueuedEvents.hasNext()) {
Throwable exception = unhandledExceptionQueuedEvents.next().getContext().getException();
Throwable rootCause = unwrapRootCause(exception);
if (rootCause instanceof ServiceValidationException) {
ServiceValidationException sve = (ServiceValidationException) rootCause;
JSFComponentUtil.markComponentAsInvalid(sve.getClientId());
// create FacesMessage here etc
...
FacesContext.getCurrentInstance().validationFailed();
return;
}
}
} catch (Exception e) {
logger.error("Error encountered while processing exception, allow default error handling to take over", e);
}
// delegate to Omnifaces Ajax exception handler
super.handle();
}
JSFComponentUtil
public static void markComponentAsInvalid(String componentId) {
UIComponent component = findComponent(componentId);
if (component != null && component instanceof EditableValueHolder) {
EditableValueHolder evh = (EditableValueHolder) component;
if (evh.isValid()) {
evh.setValid(false);
}
} else {
LOG.debug("component not found or is not instance of EditableValueHolder");
}
}
public static UIComponent findComponent(String componentId) {
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
if (viewRoot != null) {
return viewRoot.findComponent(componentId);
}
LOG.debug("View Root is null, returning null");
return null;
}
The Problem
The issue I'm running into is that upon submitting the form via the command button, the page redisplays with the input text field marked as red (expected behavior), however the text that was typed into the field is lost. I want the invalid text entered to remain in the field.

In markComponentInvalid, you can try manually setting the component's value:
evh.setSubmittedValue("aaa");
evh.setValue("aaa");
Of course, instead of hard-coding "aaa" you could add an "input" property to your ServiceValidationClass so that you can pass that value from the action method to the error handler and then to the Util class, e.g.
bean:
throw new ServiceValidationClass ("Something went wrong", ":f:input", input);
error handler:
JSFComponentUtil.markComponentAsInvalid(sve.getClientId(), sve.getInput());
etc.

Related

How to accept http requests after shutdown signal in Quarkus?

I tried this:
void onShutdown(#Observes final ShutdownEvent event) throws InterruptedException {
log.infof("ShutdownEvent received, waiting for %s seconds before shutting down", shutdownWaitSeconds);
TimeUnit.SECONDS.sleep(shutdownWaitSeconds);
log.info("Continue shutting down");
}
But after receiving ShutdownEvent Quarkus already responds with 503 to http requests. Looks like this could be done with ShutdownListener in preShutdown method. I have implemented this listener but it does not get called yet. How do I register ShutdownListener?
Use case here is OpenShift sending requests to terminating pod.
Option 1: Create Quarkus extension
Instructions are here. ShutdownController is my own class implementing ShutdownListener where I have a sleep in preShutdown method.
class ShutdownControllerProcessor {
#BuildStep
FeatureBuildItem feature() {
return new FeatureBuildItem("shutdown-controller");
}
#BuildStep
ShutdownListenerBuildItem shutdownListener() {
// Called at build time. Default constructor will be called at runtime.
// Getting MethodNotFoundException when calling default constructor here.
return new ShutdownListenerBuildItem(new ShutdownController(10));
}
}
Option 2: Modify ShutdownRecorder private static final field
New shutdown listener can be added using reflection. This is a bit ugly solution.
registerIfNeeded() need to be called after Quarkus startup, for example with timer 1 second after #PostConstruct.
#ApplicationScoped
public class ListenerRegisterer {
public void registerIfNeeded() {
try {
tryToRegister();
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
private void tryToRegister() throws NoSuchFieldException, IllegalAccessException {
final var field = ShutdownRecorder.class.getDeclaredField("shutdownListeners");
field.setAccessible(true);
final var listeners = (List<ShutdownListener>) field.get(null);
if (listeners != null && !listeners.toString().contains("ShutdownController")) {
listeners.add(new ShutdownController(10));
setFinalStatic(field, listeners);
}
}
private static void setFinalStatic(final Field field, final Object newValue) throws NoSuchFieldException, IllegalAccessException {
field.setAccessible(true);
final var modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
}

How to detect valueChange event in validator?

Mojarra 2.1
I need to write a validator for the h:inputText which performs some logic only if the value for that input is changed. I.e.
public class MyValidator implements Validator{
public void validate(FacesContext context,
UIComponent component,
Object value) throws ValidatorException;
if(valueChanged(UIComponent component)){ //The method checks if the value's changed
//do some piece of logic
}
return;
}
}
I dug into the queuing events of the UIInput and found this:
validateValue(context, newValue);
// If our value is valid, store the new value, erase the
// "submitted" value, and emit a ValueChangeEvent if appropriate
if (isValid()) {
Object previous = getValue();
setValue(newValue);
setSubmittedValue(null);
if (compareValues(previous, newValue)) {
queueEvent(new ValueChangeEvent(this, previous, newValue));
}
}
This piece of code is from the method, executed by the Validation phase callback. The first thought that popped into my head was queriyng all events fired during handling the request. The method queueEvent(FacesEvent) is implemented as follows:
public void queueEvent(FacesEvent event) {
if (event == null) {
throw new NullPointerException();
}
UIComponent parent = getParent();
if (parent == null) {
throw new IllegalStateException();
} else {
parent.queueEvent(event);
}
}
Therefore every such invokation will end up in UIViewRoot.queueEvent(FacesEvent) which is implemented as:
public void queueEvent(FacesEvent event) {
if (event == null) {
throw new NullPointerException();
}
// We are a UIViewRoot, so no need to check for the ISE
if (events == null) {
int len = PhaseId.VALUES.size();
List<List<FacesEvent>> events = new ArrayList<List<FacesEvent>>(len);
for (int i = 0; i < len; i++) {
events.add(new ArrayList<FacesEvent>(5));
}
this.events = events;
}
events.get(event.getPhaseId().getOrdinal()).add(event);
}
Which means, all events is actually stored as a List<List<FacesEvent>> for each phase. But the List<List<FacesEvent>> events is a private field, so it's impossible to get direct acces to it.
Another thing is that the actual validation is being perfromed before the quingEvent, so implemting valueChangeListener doesn't seem useful as well.
Question: Is it possible to implements such validator in JSF in a natural way?
Just do the value comparison yourself. In the validator, the old value is just readily available via UIComponent argument.
#Override
public void validate(FacesContext context, UIComponent component, Object submittedValue) {
if (component instanceof EditableValueHolder) {
Object newValue = submittedValue;
Object oldValue = ((EditableValueHolder) component).getValue();
if (newValue == null ? oldValue == null : newValue.equals(oldValue)) {
return; // Not changed, so skip validation.
}
}
// Do actual validation here.
}
If you happen to use JSF utility library OmniFaces, it has a ValueChangeValidator which does exactly this.

How can I log method expressions of JSF ajax requests

I have figured out how to log when a request is an ajax request and which page it is from, in a filter.
What I would really like to do is log what the ajax request is actually for. Such as the name of the method being called by the ajax (eg "findAddress" in this call:<p:ajax process="contactDetails" update="#form" listener="#{aboutYouController.findAddress}" .... )
How can I do this? My app has many ajax requests and I want to log which are being triggered.
public class TrackingFilter implements Filter {
private static Logger LOG = Logger.getLogger(TrackingFilter.class);
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
String pageHit = req.getRequestURI().substring(req.getContextPath().length()+1).replace(".xhtml", "");
if(!pageHit.contains("javax.faces.resource")){ // if is a url we want to log
if ("partial/ajax".equals(req.getHeader("Faces-Request"))) {
LOG.trace("ajax on URI: " + req.getRequestURI());
}
What I would really like to do is log what the ajax request is actually for. Such as the name of the method being called by the ajax (eg "findAddress" in this call:<p:ajax process="contactDetails" update="#form" listener="#{aboutYouController.findAddress}" ....)
This information is only available in the JSF component tree. The JSF component tree is only available after view build time. A view is only built when the request has been served by FacesServlet. Thus, a servlet filter is way too early as it runs before any servlet.
You'd better run the code after the restore view phase of a postback. The JSF component tree is guaranteed to be available during that moment. You can use FacesContext#isPostback() to check if the current request is a postback. You can use PartialViewContext#isAjaxRequest() to check if the current request is an ajax request. You can use the predefined javax.faces.source request parameter to obtain the client ID of the source component of the ajax request. You can use the predefined javax.faces.behavior.event request parameter to obtain the ajax event name (e.g. change, click, action, etc).
Obtaining the associated behavior listeners is in turn a story apart. This is easy on ActionSource2 components (e.g. <h|p:commandButton action="#{...}">) as the MethodExpression is just available by ActionSource2#getActionExpression(). However, this isn't easy on BehaviorBase taghandlers (e.g. <f|p:ajax listener="#{...}">) as this API doesn't have any method like getBehaviorListeners(). There are only methods to add and remove them, but not to obtain a list of them. So some nasty reflection trickery is necessary to access the private field with those listeners whose name is JSF implementation specific. In Mojarra it's listeners and in MyFaces it's _behaviorListeners. Both are fortunately assignable from List and it's the only field of that type, so we could just check for that. Once having hand of the BehaviorListener instance, then you still need to do another reflection trickery to obtain the MethodExpression field of that instance. Yuck.
All in all, here's how the trickery look like in flavor of a PhaseListener listening on afterPhase of RESTORE_VIEW:
public class AjaxActionLoggerPhaseListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
if (!(context.isPostback() && context.getPartialViewContext().isAjaxRequest())) {
return; // Not an ajax postback.
}
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
String sourceClientId = params.get("javax.faces.source");
String behaviorEvent = params.get("javax.faces.behavior.event");
UIComponent source = context.getViewRoot().findComponent(sourceClientId);
List<String> methodExpressions = new ArrayList<>();
if (source instanceof ClientBehaviorHolder && behaviorEvent != null) {
for (ClientBehavior behavior : ((ClientBehaviorHolder) source).getClientBehaviors().get(behaviorEvent)) {
List<BehaviorListener> listeners = getField(BehaviorBase.class, List.class, behavior);
if (listeners != null) {
for (BehaviorListener listener : listeners) {
MethodExpression methodExpression = getField(listener.getClass(), MethodExpression.class, listener);
if (methodExpression != null) {
methodExpressions.add(methodExpression.getExpressionString());
}
}
}
}
}
if (source instanceof ActionSource2) {
MethodExpression methodExpression = ((ActionSource2) source).getActionExpression();
if (methodExpression != null) {
methodExpressions.add(methodExpression.getExpressionString());
}
}
System.out.println(methodExpressions); // Do your thing with it.
}
private static <C, F> F getField(Class<? extends C> classType, Class<F> fieldType, C instance) {
try {
for (Field field : classType.getDeclaredFields()) {
if (field.getType().isAssignableFrom(fieldType)) {
field.setAccessible(true);
return (F) field.get(instance);
}
}
} catch (Exception e) {
// Handle?
}
return null;
}
}
In order to get it to run, register as below in faces-config.xml:
<lifecycle>
<phase-listener>com.example.AjaxActionLoggerPhaseListener</phase-listener>
</lifecycle>
Above is tested and compatible with Mojarra and PrimeFaces and theoretically also compatible with MyFaces.
Update: in case you're using JSF utility library OmniFaces, or are open to, since version 2.4 you can use the new Components#getCurrentActionSource() utility method to find out the current action source component and Components#getActionExpressionsAndListeners() to get a list of all action methods and listeners registered on a given component. This is also useable on regular (non-ajax) requests. With that, the above PhaseListener example can be reduced as below:
public class FacesActionLoggerPhaseListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.PROCESS_VALIDATIONS;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
if (!event.getFacesContext().isPostback())) {
return;
}
UIComponent source = Components.getCurrentActionSource();
List<String> methodExpressions = Components.getActionExpressionsAndListeners(source);
System.out.println(methodExpressions); // Do your thing with it.
}
}

how to populate cvc-complex-type error information in mule

I have mule message filters to validate SOAP Request against schema,
my question is:
how to log error details of 'which element is have parse exception (cvc-complex-type). because catch block is always trowing exception details: "Message has been rejected by filter". how can i show exact exception?. so that user will enter correct data on request.
write a customfilterclass as a child of SchemaValidationfilter.
override accept method and throw soap fault.
public class CustomSchemaValidationFilter extends SchemaValidationFilter {
#Override
public boolean accept(MuleMessage muleMessage) {
return validateBody(muleMessage);
}
public boolean validateBody(MuleMessage message) throws Fault {
Source source = null;
try {
source = loadSource(message);
} catch (Exception e) {
throw getFaultObj(e);
}
try {
createValidator().validate(source);
} catch (SAXException e) {
throw getFaultObj(e);
} catch (IOException e) {
throw getFaultObj(e);
}
return true;
}
private Fault getFaultObj(Exception e) {
return new SoapFault(e.getMessage(), e, new QName(
DEFAULT_SCHEMA_LANGUAGE));
}
}
//and add in mule flow following tags to validate filter.
<custom-filter class="org.mule.module.xml.filters.CustomSchemaValidationFilter">
<spring:property name="schemaLocations" value="${approvalHistorySchemaLocation}" />
</custom-filter>**

Programmatically created form and validation, unhandled faces messages

I create a form by using Apache MyFaces library. The form is related to the
jsf-page by a binding. Moreover I built a TestValidator-class which implements
Validator-interface. My form got shown, I enter my input, validation gots
triggered successfully. Unfortunately I am not able to display corresponding
FacesMessage. I guess I am not able to tell JSF 'Please rerender h:messages
after throwing the validator-Exception'.
What went wrong? Thanks in advance.
#FacesValidator("TestValidator") // class TestValidator
public class TestValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent uiComponent, Object value) throws ValidatorException {
System.out.println("TestValidator.validate: " + value);
if (((String) value).equals("2")){
FacesMessage fm = new FacesMessage();
fm.setSeverity(FacesMessage.SEVERITY_ERROR);
fm.setSummary("TestValidator FEHLER");
fm.setDetail("TestValidator FEHLER");
throw new ValidatorException(fm);
}
}
}
<!-- JSF-Snippet -->
<t:div id="formContainer" binding="#{formsbuilder.form}" />
private Div createFacesMessagesDiv() { // FormsBuilder-Bean-Snippet for creating the container for the FacesMessage
facesMessagesDiv = new Div(); // CORRESPONDING BEAN PROPERTY
facesMessagesDiv.setId("facesMessagesContainer");
facesMessagesDiv.setStyle("color: blue; border: 3px solid green;");
HtmlOutputText introText = new HtmlOutputText();
introText.setId("facesMessagesIntroText");
introText.setValue("FacesMessages - Start: ");
facesMessagesDiv.getChildren().add(introText);
HtmlMessages fms = new HtmlMessages();
fms.setId("facesMessages");
facesMessagesDiv.getChildren().add(fms);
HtmlOutputText outroText = new HtmlOutputText();
outroText.setId("facesMessagesOutroText");
outroText.setValue("FacesMessages - Ende!");
facesMessagesDiv.getChildren().add(outroText);
return facesMessagesDiv;
}
private HtmlAjaxCommandLink createSaveFormButton() { // saveButton after hitting this button, facesMessages should show up;
HtmlAjaxCommandLink saveFormButton = HtmlRendering.createGeneralButton("saveForm" + currentSubForm.getIdAsString(), /* ID */
"#{resources.labels['formsgenerator_saveForm']}",
"#{formsbuilder.submitForm}",
null, /* action */
null, /* actionReturnType */
null, /* onComplete */
"", /* styleClassValueExpression */
"", /* imageValue */
"modifiedContainerDiv");
saveFormButton.setReRender(facesMessagesDiv); // CORRESPONDING BEAN PROPERTY
saveFormButton.setValueExpression("oncomplete", HtmlRendering.createValueExpression("afterGeneralSavingSubForm()");
return saveFormButton;
}
It does not look like you are setting the message correctly.
You forgot to add the FacesMessage to the FacesContext.
Try this:
public void validate(FacesContext context, UIComponent uiComponent, Object value) throws ValidatorException {
System.out.println("TestValidator.validate: " + value);
if (((String) value).equals("2")){
FacesMessage fm = new FacesMessage();
fm.setSeverity(FacesMessage.SEVERITY_ERROR);
fm.setSummary("TestValidator FEHLER");
fm.setDetail("TestValidator FEHLER");
FacesContext.getCurrentInstance().addMessage(null, fm);
throw new ValidatorException(fm);
}
}
Here's a more concise way to write a FacesMessage:
public void validate(FacesContext context, UIComponent uiComponent, Object value) throws ValidatorException {
System.out.println("TestValidator.validate: " + value);
if (((String) value).equals("2")){
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "TestValidator FEHLER", "TestValidator FEHLER"));
throw new ValidatorException(fm);
}
}

Resources