Catching global "#In attribute requires non-null value" in SEAM 2 - java

On PAGE A is a table with some data from the database.
by clicking on a row, the page will be forwarded to PAGE B and in the controller the entity will be injected
#DataModelSelection(value = ENTITY_LIST_NAME)
#Out(value = ENTITY_NAME, scope = ScopeType.CONVERSATION, required = false)
private Entity entity;
this works as it should.
The problem is, that our users seems to use bookmark for PAGE B, so the entity will never be injected because they never visited PAGE A
so they always throw this exception
#In attribute requires non-null value
Is there a global function to catch all #In attribute requires non-null value exceptions and forward the user to PAGE C (startpage)?
(of course i can catch this execption on PAGE B but this happens not only on one page, we want to handle this exception on every page)
we are using: jboss5 + jsf 1.2 + seam 2
UPDATE after the answer of EmirCalabuch:
I also tried the hint from EmirCalabuch with:
<page conversation-required="true" no-conversation-view-id="PageC.xhtml" />
but the problem is, that the conversation is alive at this moment, to this forwarding to pageC never happens...
i also made in the page.xml of this page something like:
<action execute="#{controller.checkIfEntityIsSet()}" />
<navigation>
<rule if-outcome="HOME">
<redirect
view-id="/pages/home.xhtml"
/>
</rule>
</navigation>
and in my Controller.java i have somthing like this:
public String checkIfEntityIsSet(){
if(getEntity() == null){
return "HOME";
}
return "";
}
but this checkIfEntityIsSet() is never called, because the #In attribute requires non-null value is thrown before... so this was not a help at all...

Exception handling rules are specified in pages.xml. You could include a rule to catch the org.jboss.seam.RequiredException that is thrown for that type of error and perform your navigation to page C in it.
This however is not a very clean solution as you would bind that exception to that page and most probably you will have this exception elsewhere and would like to redirect to a different page.
A simpler way of achieving the same result is making the conversation required in PageB.page.xml and specifying the view to redirect to when no conversation is active. The page descriptor has an option that allows you to do just that (on PageB.page.xml):
<page conversation-required="true" no-conversation-view-id="PageC.xhtml" />
This tells Seam that if a user tries to display page B and there is no conversation active (which happens when the user gets there from a bookmark), then redirect the user to PageC.xhtml.
Anyway, it takes very little effort to make the page bookmarkable (if you feel your users will be bookmarking it a lot), using page parameters and actions, for example:
In your list page A, instead of an h:commandLink or h:commandButton for each of the rows that take you to page B, use s:link or s:button:
<h:dataTable var="var" value="#{myList.dataModel}">
...
<s:link value="PageB.xhtml">
<f:param name="id" value="#{var.id}" />
</s:link>
...
</h:dataTable>
This will create a link to Page B for each of the entities in the list, passing its ID (for example, PageB.seam?id=1 in the first row, PageB.seam?id=2 in the second and so on. These links are bookmarkable.
On PageB.page.xml declare the parameter:
<param name="id" value="#{myHomeComponent.id}" />
Where myHomeComponent is a component of type EntityHome<YourEntity>. You can then use #{myHomeComponent.instance} inside Page B to access the entity selected.

i managed it now different:
in the Controller.java i have for the initialization something like this:
#Create
public void initialize() throws MissingMyEntityException {
if(qualifiedCustomer == null){
throw new MissingMyEntityException("something wrong");
}
....
}
my MissingMyEntityException.java looks like this:
public class MissingMyEntityException extends Exception {
private static final long serialVersionUID = 8640645441429393157L;
public MissingMyEntityException(String message) {
super(message);
}
}
and in the pages.xml i have the exception handler like this:
<exception class="com.dw.companyName.view.xyz.exception.MissingMyEntityException">
<redirect view-id="/pages/home.xhtml">
<message>something went wrong</message>
</redirect>
</exception>
and this fixed the problem.
but thanks for your help, but it was not working your way :(

Related

message from validator class does not appear in messages control on xpage?

I have created a class to test a person object with. e.g.
public class PersonValidators implements Serializable {
private static final long serialVersionUID = 1L;
public PersonValidators(){
}
public void valIdNumber(FacesContext facesContext, UIComponent component, Object value) {
System.out.println(this.getClass().getSimpleName().toString() + " - valId(...), value=" + value.toString());
String msg = "Invalid id for person provided";
FacesContext.getCurrentInstance().addMessage(null, new javax.faces.application.FacesMessage(javax.faces.application.FacesMessage.SEVERITY_INFO, msg, ""));
}
}
And I have registered as a managed bean as followed:
<managed-bean>
<managed-bean-name>personValidators</managed-bean-name>
<managed-bean-class>com.mybank.mycard.test.PersonValidators</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>
For my control I have defined to use the method as Validator:
<xp:inputText id="idNumber"
value="#{personBean.person.idNumber}"
disabled="#{!personBean.person.editable}"
validator="#{personValidators.valIdNumber}" required="true">
<xp:this.validators>
<xp:validateRequired
message="Field PassNumber is empty">
</xp:validateRequired>
</xp:this.validators>
</xp:inputText>
On the xpage I have added a messages control:
<xp:panel
rendered="#{javascript:facesContext.getMessages().hasNext()}"
style="margin-top:15px;">
<div class="row">
<div class="col-md-12">
<xp:panel>
<xp:this.styleClass><![CDATA[#{javascript:return "alert alert-danger"}]]></xp:this.styleClass>
<xp:messages id="msgBox"></xp:messages>
</xp:panel>
</div>
</div>
</xp:panel>
In the xsp.properties I have set:
xsp.client.validation=false
The problem is that the message from my validator class NEVER appears in the messages control.
What do I have overlooked?
UPDATE:
I did not post the code that initializes the validation:
<xp:button value="Save" id="SaveButton"
styleClass="btn-primary" rendered="#{personBean.person.editable}">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:confirm
message="Are you sure you want to submit this case?" />
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:personBean.save();
facesContext.getExternalContext().redirect("person.xsp?custId=" + personBean.getPerson().getCustomer().getCustId())}]]></xp:this.script>
</xp:executeScript>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
When I remove the facesContext... the message is displayed. If I add it the xpages will be re-routed.
How can I prevent this?
I though the XPage would not be continue with the next phase when a message was added to the facescontext :-?
I have reproduced your code snippets and the message from valIdNumber is successfully displayed in messages control. The only difference that I've set the value of inputText component to the viewScope variable instead of managed bean field and removed the disabled property, but that should not matter.
The method won't fire if inputText has no value entered, in this case only validateRequired's message will be displayed.
Do you always see the println output in server console as the evidence that the method was at least fired?
Update:
This happens because in your case the InvokeApplication phase, where your redirect (and save!) are done, is executed anyway, even if your validator fails. You should explicitly tell the engine that you don't want to proceed.
Add the following lines to your validation method before return:
UIInput myInput = (UIInput)component;
myInput.setValid(false);
These lines should execute in the same conditional block where FacesContext.addMessage executes, i.e. when validation fails.
One more solution is to throw a ValidatorException instead when your check fails, it will automatically set the component as invalid and will tell the lifecycle management process to jump to RenderResponse phase, avoiding UpdateModel and InvokeApplication phases. You can also provide your custom message in ValidatorException constructor and it will be automatically added to FacesContext:
throw new javax.faces.validator.ValidatorException(
new FacesMessage("Invalid id for person provided")
);

How to deal with 'cached' instance in #ViewScoped page?

App running with JSF, Primefaces, eclipselink, not a small app, about 100 pages/bean all working perfectly
I got some troubles understanding how my #ViewScoped page works, I got a select UI component, filled with a simple List<People> and a back-end selectedPeople in my bean
// all getters, setters, JPA annotations, all good
public class People {
private String name;
private List<Car> cars;
}
#ManagedBean
#ViewScoped
public class PeopleBean {
#EJB
private Service sPeople;
private People selectedPeople;
private List<People> listPpl;
#PostConstruct
public void init(){
listPpl = sPeople.readAll(); // always good, same as DB values
}
public People getSelectedPeople(){
return selectedPeople;
}
public People setSelectedPeople(People p){ // p is an old element
selectedPeople = p; // BREAKPOINT
}
// getter for the list too
public void method(){
Logger.getAnoymousLogger().severe(selectedPeople.getCars()); // the one the old people, not the ne contained in the actual list
}
}
<p:selectOneMenu id="selectP" value="#{peopleBean.selectedPeople}" converted="#{genericSecuerdConverter}" >
<p:ajax event="change" partialSubmit="true" listener="#{peopleBean.method()}" />
<f:selectItems value="#{peopleBean.listPpl}" var="people" itemLabel="#{people.name}" itemValue="#{people}" />
</p:selectOneMenu>
Sequence of use and problem is (information taken from debugging) :
go to peoplePage.xhtml where the select element is, IDs of the list's element are #410, #411, #412 (3 peoples)
go to modif.xhtml, change the 3rd people (remove a car, saved in DB (check in DB))
come back to peoplePage.xhtml, list is OK, IDs in debug are #650, #651, #652
change the value (from null) of the selectUI to choose a people, and at the breakpoint, p appears to be the #412 element, so the changes on its car's list are not visible, it does not come from the listPpl (because contains only valid elements and corresponds to DB), it's kind of caching
I tried to disable ecpliselink cache as states EclipleLink cache
change eclipselink property
change JPA propery
use #Cacheable(false)
No one had an effect, nor go to private navigation neither clear the browser cache and come back to the page, the p element is still the old one from first loading
I thought #ViewScoped allows to open a page each time as if it was the first time, but seems not, can't figure where the element can be stored/cached
Edit I used a workaround for the moment but this is obviously o the best solution
public People setSelectedPeople(People p){
if(p!=null)
selectedPeople = sPeople.read(p.getId());
}
What you are looking for is #RequestScoped. It will create everything each and every time you do a suitable HTTP request. Otherwise it is not guaranteed to destroy the #ViewScoped beans. An example in the Omnifaces documentation: ViewScoped.
This feature could be used to help the recreation of the page when the user is using the back and forward buttons of the browser for example.
#RequestScoped
Bean lives as long as the HTTP request-response lives. It gets created upon a HTTP request and gets destroyed when the HTTP response associated with the HTTP request is finished.
#ViewScoped
Bean lives as long as the user is interacting with the same JSF view in the browser window/tab. It gets created upon a HTTP request and gets destroyed once the user postbacks to a different view.
Source of descriptions: https://www.tutorialspoint.com/jsf/jsf_managed_beans.htm

Primefaces Schedule - Extracting data from event

After a day of pain I finally got data into the schedule event.
I'm now trying to set event when it's clicked, it loads a new page using the data variable.
Here's the xhtml
<p:schedule id="schedule"
value="#{LeadListScheduleController.eventModel}" widgetVar="schedule">
<p:ajax event="eventSelect"
listener="#{LeadListController.redirectToLeadEdit(Data)}" />
</p:schedule>
And the Java
public void redirectToLeadEdit(String data) throws IOException {
redirect("/lead/" + data + "/edit");
}
How can I get the data object that's now in the event so the url works?
As the showcase explains, there's no need of manually passing the current selected event as an argument to the listener method. Just declare a method where you catch the incoming event and get the data from it. After that, you can call your redirect method with everything you need.
<p:ajax event="eventSelect"
listener="#{LeadListController.onEventSelect}" />
public void onEventSelect(SelectEvent selectEvent) {
ScheduleEvent event = (ScheduleEvent) selectEvent.getObject();
redirect(event.getData());
}
Appart from that and totally unrelated to the problem, I encourage you to name your managed beans as the bean class name with the first letter lowercased. It's a java specification that has been already discussed here.

How to set action variable value from jsp

My code is like this:
public class CustoTypeAction extends ActionSupport{
private ResultDto resultDto;
//setter & getter resultDto
}
Config:
<action name="listCustoType" method="listCustomerType" class="custoType">
<result name="success">listCustoType.jsp</result>
How can I set resultDto value to null from jsp on page load? What struts tag should I use?
As I understood you must implement Preparable interface. It executes before execute() method.
http://struts.apache.org/2.1.6/struts2-core/apidocs/com/opensymphony/xwork2/Preparable.html
If you want to show the object only once, and it must be gone after user reloads the page, you must use session. If file is snown already, you can change the value in session that it must not be shown on next reload.
You can see the example to work with session from here:
http://www.roseindia.net/struts/struts2/struts2-login.shtml

wicket onrendered values [duplicate]

1) i have added an element in request object given below, i need to get/read this in my webpage, how can i do it?
<input type="hidden"> wicket:id="submitted" value="false" />
eg: in servlet, use request.getParameter("submitted") from hidden session.
2) in my controller class i want to set the value in session or hidden field, so that i can identify the user if he already processed the request or enetered my block of code.
1) use HiddenField
2) use a custom WebSession object:
public class MySession extends WebSession{
public Mysession(Request request){super(request);}
private boolean completedRegistration;
public boolean hasCompletedRegistration() {
return completedRegistration;
}
public void setCompletedRegistration(boolean completedRegistration) {
this.completedRegistration = completedRegistration;
}
}
I am not sure I have fully understood your questions.
But to make it short, if you want to get the value stored in your request object, you'll need to make the model of your input map to this value (by using HiddenField wicket internal class).
If you want to track your user, the best thing to do is looking around for a cookie set on the client side that'll allow you to manage its visits.
Please refine your question if you want a more precise answer...

Categories