Is it possible to tell which component was clicked that caused the ViewExpiredException to be thrown when using AJAX? I'd like to avoid showing the session expired page if the user simply tried to log out and show him the normal log out page instead.
I was thinking of overriding shouldHandleExceptionRootCause but I cannot find anything to identify if the logout button was pressed or not.
I'm using Mojarra JSF 2.2 and Omnifaces 2.5.1.
Solution
#Override
protected boolean shouldHandleExceptionRootCause(FacesContext context, Throwable exception)
{
UIComponent source = Components.getCurrentActionSource();
if (source != null && "logout".equals(source.getId()))
{
NavigationHandler nh = context.getApplication().getNavigationHandler();
nh.handleNavigation(context, null, "logout.xhtml?faces-redirect=true");
return false;
}
else
{
return super.shouldHandleExceptionRootCause(context, exception);
}
}
The Components utility class offers several methods to identify the currently submitted form, the currently invoked command and the source component of the action event (which can be the command component itself, but also an input component in case of an ajax event).
UIForm form = Components.getCurrentForm();
UICommand command = Components.getCurrentCommand();
UIComponent source = Components.getCurrentActionSource();
Take your pick. You could for example check the id of the form, command or source to see if it's the desired one.
Related
I need a technique in which I can call java method for retrieving information from DB and displaying message in dialog box and by pressing "Next" button, if have any next message it will appear. That java method calls automatically after a specific time, says first time when user logged-in and then after ever 3 min. This I want in JSF & PrimeFaces.
I used ScheduleExecutarService for scheduling and my run method as below
public void run() {
try {
System.out.println("beep");
notificationList = retrieveNotificationsFromDB(userId, groupCode, functionCode, chooseNotificationType());
if(notificationList != null && !notificationList.isEmpty()){
showPopup();
}
} catch(Exception e) {
LOGGER.error(e.getMessage());
}
}
public void showPopup() {
if (notificationList != null && !notificationList.isEmpty()) {
setNotificatiomMessage(notificationList.get(0).getMessage());
notificationList.remove(0);
RequestContext.getCurrentInstance().execute("PF('dialogVar').show()");
}
}
In showPopup() I'm not able to get the object of RequestContext
Other Way I tried <p:poll> tag, I read that about the tag is there is a problem for stopping it.
Please guide me what I should use.
In showPopup() I'm not able to get the object of RequestContext
Because there is no active HTTP request (from the client to the server) which is handled at this point.
Solution:
Let the Scheduler fire a CDI event and catch this event with a CDI observer in your client bean.
Now you have to options: Either let the JSF view poll your client bean to ask for new messages, or you create a socket, over which the client bean may inform the JSF view about new messages. You are able to handle the incoming message on your JSF view via JavaScript and do with it whatever you want.
For a very basic example for sockets, see here:
http://www.primefaces.org/showcase/push/counter.xhtml
See also:
https://docs.oracle.com/javaee/6/tutorial/doc/gkhic.html
Finally I got the simple solution with these 2 line of code
<p:poll id="initNotificationAlert" interval="1" oncomplete="PF('initNotificationVar').stop()" listener="#{notificationView.retrieveAlertNotification()}" widgetVar="initNotificationVar"></p:poll>
<p:poll id="notificationAlert" autoStart="false" interval="180" listener="#{notificationView.retrieveAlertNotification()}" widgetVar="notificationVar"></p:poll>
first <p:pol> for initial message and second for <p:pol> for rest.
if any better solution of improvement need in this solution improve it.
Thanks
You can open dialog manually like this:
Scheduler
There are many schdulers like Quartz or for WebApplication
Dialog
public String showDialog(String dialogName){
Map<String, Object> options = new HashMap<String, Object>();
options.put("modal", true);
options.put("closable", true);
options.put("draggable", true);
options.put("resizable", true);
options.put("contentHeight", 386);
options.put("contentWidth", 1266);
RequestContext.getCurrentInstance().openDialog(dialogName, options, null);
return "";
}
While dialogName is that .xhtml page you are trying to show. You just need a schedular to call this method and dialog will appears.
When I click on submit button for a jsf page from a jsf portlet, mymethod of jsf bean is invoked.
<h:commandButton id="id1" action="#{Mybean.mymethod}" value="Click" />
public String mymethod() {
FacesContext fc = FacesContext.getCurrentInstance();
Object obj = fc.getExternalContext().getResponse();
if (obj instanceof ActionResponse){
System.out.println("ActionResponse !");
} else if (obj instanceof RenderResponse) {
System.out.println("RenderResponse !");
}
}
But none of the types its satisfying for Response object. What type of response is it?
Beause I am trying to figure if it is ActionResponse, then I need to set setEvent method.
And I guess it should be ActionResponse type right? I wonder why its not.
<-Modified->
Now I am getting Action Response. Might be because I have jsf 1.2 instead of 1.1 and I am in different system.
For a Portlet A, I have set the event in bean as follows,
ar.setEvent("showResults", "true"); //ar is ActionResponse
Then in Portlet class for Portlet B, I am doing as follows,
public class IPC extends GenericFacesPortlet {
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
if (request.getAttribute("showResults") == "true") {
request.setAttribute("javax.portlet.faces.viewId", "/JsfB_alt.jsp");
}
super.doView(request, response);
}
#ProcessEvent
public void processIPCEvent(EventRequest eRequest, EventResponse eResponse)
throws PortletException, IOException
{
Event event = eRequest.getEvent();
if(event.getName().equals("showResults")){
System.out.println("Event encountered !");
eRequest.setAttribute("showResults", "true");
}
}
}
So what I am trying to do is change from default view of Portlet B from /JsfB.jsp to /JsfB_alt.jsp when I click the button in Portlet A by setting an event.
But event is not listened as I can see that "Event encountered" is not getting printed.
I even tried changing processIPCEvent to processEvent with #Override but still its not called. I hope it will be called automatically when there is any event, right?
I feel problem in setting event in ar.setEvent()
What do you think?
FYI, I am using weblogic server 10.3 and jsf 1.2
Please suggest what could be the problem.
This is probably due to the portlet bridge implementation being used in your application.
Since the JSF implementations are written to work with the servlet API, they won't work out of the box in a portlet. So, when a context is created the portlet response will likely be adapted to a servlet response. The context merely returns the objects passed to it at creation time.
In the bridges I've used (IBM implementations) these wrappers also implement the portlet interfaces but I'm not aware of a requirement to do so.
You may want to state your platform, libraries and their versions if you want a more informed answer.
I defined as java event (jsr) using <event-definition> and <supported-processing-event> in portlet.xml and now I am able to listen to it.
I have three radio buttons, an inputText and a submit button. I only want to validate the input text on submit when a certain radio is selected. So I have
<h:inputText validator="#{myBean.validateNumber}" ... />
And in my bean I have
public void validateNumber(FacesContext context, UIComponent component,
Object value) throws ValidatorException{
if(selectedRadio.equals("Some Value"){
validate(selectedText);
}
}
public void validate(String number){
if (number != null && !number.isEmpty()) {
try {
Integer.parseInt(number);
} catch (NumberFormatException ex) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Error", "Not a number."));
}
} else {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Error", "Value is required."));
}
}
The one thing that make this not work is that when I submit, validateNumber(...) runs before my setter method for the radio button setSelectedRadio(String selectedRadio). Therefore causing this statements
if(selectedRadio.equals("Some Value"){
validate(selectedText);
}
to not execute correctly. Any idea on how to get around this problem?
The selectedRadio is as being a model value only updated during update model values phase, which is after the validations phase. That's why it's still the initial model value while you're trying to examine it.
You'd have to grab it from either the request parameter map (which is the raw submitted value), or the UIInput reference, so that you can get either the submitted value by getSubmittedValue() or the converted/validated value by getValue().
So,
String selectedRadio = externalContext.getRequestParameterMap().get("formId:radioId");
or
UIInput radio = (UIInput) viewRoot.findComponent("formId:radioId"); // Could if necessary be passed as component attribute.
String submittedValue = radio.getSubmittedValue(); // Only if radio component is positioned after input text, otherwise it's null if successfully converted/validated.
// or
String convertedAndValidatedValue = radio.getValue(); // Only if radio component is positioned before input text, otherwise it's the initial model value.
It is called cross-field validation (validate not only based in the value of a component, but a set of them).
Currently, JSF2 does not support it ( JSF doesn't support cross-field validation, is there a workaround? ) but there are several libraries (in the refered question omnifaces is mentioned, it looks like seamfaces also has something for it) that might help. Also in the question there is a workaround.
I have a jsf composite component implemented from two p:calendar components.
The idea is when the first calendar is selected, the value of the second calendar need to be reset. There is a problem when the validation takes place, and the reset of the second calendar is not performed.
After reading posts I decided to use EditableValueHolder in my validator.
I have custom validator: in which I added the following code:
#Override
public void validate(FacesContext fc, UIComponent uic, Object o) throws ValidatorException {
//....
resetValues(fc);
}
public void resetValues(FacesContext fc) {
PartialViewContext partialViewContext = fc.getPartialViewContext();
Collection<String> renderIds = partialViewContext.getRenderIds();
UIComponent input;
UIViewRoot viewRoot = fc.getViewRoot();
for (String renderId : renderIds) {
input = viewRoot.findComponent(renderId);
if (input.isRendered() && input instanceof EditableValueHolder) {
EditableValueHolder editableValueHolder = (EditableValueHolder) input;
editableValueHolder.setSubmittedValue(null);
editableValueHolder.setValue(null);
editableValueHolder.setValid(true);
editableValueHolder.setLocalValueSet(false);
}
}
}
After debug I can see that each code line is passed, but nothing is happening on jsf side.
This is not the right moment to reset the values. They will be overridden anyway for the current component after the validate() method leaves and also for the second calendar once it get validated. You need to perform the reset somewhere after the update model values phase, preferably before the invoke action phase, so that you've chance to change the model value in an action(listener) method. You could use an ActionListener or a PhaseListener for this.
By the way, the JSF utility library OmniFaces has a reuseable solution for this in flavor of ResetInputAjaxActionListener.
I have an issue with the attributes values of a validator component.
Apparently the validator is created when I first visit a page.
Please see my code below:
<h:inputText value="#{qsetting.value}" rendered="#{qsetting.dataType=='Double'}">
<mw:validateRange min="#{qsetting.minValue}" max="#{qsetting.maxValue}" />
</h:inputText>
The inputText component is rerendered through ajax but apparently, including the value that is displayed.
Unfortunately, the qsetting.minValue and qsetting.maxValue are not refreshed, causing my validator to not work correctly.
Is there a possibility to refresh the validator, to make sure it re-retrieves its attributes or to just create a new instance of the validator?
The validator class itself is currently implementing "Validator, Serializable".
Also, I'm using jsf1.2 with facelets...
Thanks,
Steven
I've hit this problem in a non-ajax environment a few times over the years, and hit it again today. The addition of Ajax doesn't really change anything since a validator attribute is never evaluated again once the page is initially built, ajax or otherwise.
The only solution I've come up with is to set the validator attribute to a validator expression, then evaluate that expression inside the validate method.
One other issue I hit (also with JSF 1.2 and Facelets) is that not all EL variables worked. I had to use a static managed bean as the root of my expression to access the value. A facelet ui:param value as a root would not work. I haven't tested to see what else may not correctly evaluate. This could be due to another bug in the design of JSF itself. See http://myfaces.apache.org/core12/myfaces-api/apidocs/javax/faces/context/FacesContext.html#getELContext%28%29.
For example, instead of:
max="#{qsetting.maxValue}"
use
maxExpression="qsetting.maxValue"
Then
public String getMax(FacesContext context) {
Application app = context.getApplication();
ExpressionFactory exprFactory = app.getExpressionFactory();
ValueExpression ve = exprFactory.createValueExpression(context.getELContext(),
"#{" + getMaxExpression() + "}",
String.class);
Object result = ve.getValue(context.getELContext());
return (String)result;
}
public String getMaxExpression() {
return this.maxExpression;
}
public void setMaxExpression(String maxExpression) {
this.maxExpression = maxExpression;
}
//// StateHolder
public boolean isTransient() {
return isTransient;
}
public void setTransient(boolean newTransientValue) {
isTransient = newTransientValue;
}
public Object saveState(FacesContext context) {
Object[] state = new Object[1];
state[0] = maxExpression;
return state;
}
public void restoreState(FacesContext context, Object state) {
Object[] values = (Object[]) state;
maxExpression = (String) values[0];
}
UPDATE 2012-09-19:
After investigating how MyFaces Commons solves this problem, the better solution is to change the rules Facelets uses to evaluate validator and converter attribute expressions.
It basically comes down to adding a new validator or converter MetaRule which, when applied, checks to see if the attribute value is non-literal. If it is non-literal, call a special method on your validator or converter which passes in the value expression rather than the current value.
http://svn.apache.org/viewvc/myfaces/commons/trunk/myfaces-commons-validators/src/main/java/org/apache/myfaces/commons/validator/_ValidatorRule.java?view=markup
The validator at that point needs to store the value expression as state and evaluate it when needed. MyFaces commons provides all of the complicated infrastructure to make this happen generically, but you could dump all of that and write a simple custom rule and directly manage the ValueExpression yourself, similar to what I originally posted.