JSF request scope with get parameter - jsf

i know there are many similar threads but no like mine:
I have a requestscope bean:
#ManagedBean
#RequestScoped
public class Bean implements Serializable{
private String username = ""; //managed by textbox
private String password = ""; //managed by textbox
private String id ="-";
//Load the Parameter as usual:
#PostConstruct
public void fetchParams(){
System.out.println("FETCH PARAMS");
FacesContext facesContext = FacesContext.getCurrentInstance();
String id = facesContext.getExternalContext().getRequestParameterMap().get("id");
if(id == null || id.length() == 0) return;
setId(id);
}
// getters & setters
public void doSomething(){ //executed when clicked on the sumbit-button on the jsf-site
StaticFunctions.doSomething(this);
}
}
The code does following:
it retrieves the get-parameter "id" and saves it into String id (confirmed by string.out....).
But when the method doSomething() is executed and the previously stored "id" is read and returns "-" (like nothing happened).
why is this so?

Your ManagedBean is #RequestScoped and will be destroyed at the end of the request. When doSomething() is executed the user submitted the form and started a new request.
So you should see "FETCH PARAMS" twice in the console because two Beans are created but for the second request id is null.
You can find a detailed explanation about the four JSF-Scopes here.

Related

Create a new FlowScoped bean when other one was not finished

I am working whith JSF (Primeface) and j2ee on weblogic.
So, i have two different flows in my application:
Flow configuration:
public class RequestFlow implements Serializable {
#Produces
#FlowDefinition
public Flow defineFlow(#FlowBuilderParameter FlowBuilder flowBuilder) {
String flowId = "requestFlow";
flowBuilder.id("", flowId);
flowBuilder.viewNode(flowId, "/inside/customer/request/flow/requestFlow.xhtml").markAsStartNode();
flowBuilder.viewNode("requestFlowCart", "/inside/customer/request/flow/requestFlowCart.xhtml");
flowBuilder.viewNode("requestFlowCheckout", "/inside/customer/request/flow/requestFlowCheckout.xhtml");
flowBuilder.returnNode("finishRequest").fromOutcome("/inside/customer/request/requests.xhtml");
return flowBuilder.getFlow();
}
}
CDI's flow bean:
#Named
#FlowScoped("requestFlow")
public class RequestFlowBean implements Serializable {
//some logic
}
Second configuration:
public class OrderFlow implements Serializable {
#Produces
#FlowDefinition
public Flow defineFlow(#FlowBuilderParameter FlowBuilder flowBuilder) {
String flowId = "orderFlow";
flowBuilder.id("", flowId);
flowBuilder.viewNode(flowId, "/inside/customer/order/flow/orderFlow.xhtml").markAsStartNode();
flowBuilder.viewNode("orderFlowSelectRequests", "/inside/customer/order/flow/orderFlowSelectRequests.xhtml");
flowBuilder.viewNode("orderFlowReviewRequests", "/inside/customer/order/flow/orderFlowReviewRequests.xhtml");
flowBuilder.viewNode("orderFlowCheckoutOrder", "/inside/customer/order/flow/orderFlowCheckoutOrder.xhtml");
flowBuilder.returnNode("finishOrder").fromOutcome("/inside/customer/order/orders.xhtml");
return flowBuilder.getFlow();
}
}
CDI's flow bean:
#Named
#FlowScoped("orderFlow")
public class OrderFlowBean implements Serializable {
//some logic
}
My Case:
User opens page where by clicking h:button starts the "requestFlow" (doesn't finish it!)
Using menu navigates to another page, by clicking h:button tries to start the "orderFlow".
Problem:
"OrderFlow" wasn't start without any error in console! And the first flow still in memory, but according documentation it have to be destroyed.
So, I want to be able create a new FlowScoped bean when other one was not finished.
Any suggestions?
So, exactly in 2 month i found the answer.
The trick is how you start your flow. If you want to run new JSF flow, while didn't finish other one, you have to remove from JSF context previous instances of any flow. In order to do it, you have add method in controller:
public String initFlow() {
FacesContext context = FacesContext.getCurrentInstance();
FlowHandler handler = context.getApplication().getFlowHandler();
ExternalContext extContext = context.getExternalContext();
String sessionKey = extContext.getClientWindow().getId() + "_flowStack";
Map<String, Object> sessionMap = extContext.getSessionMap();
if (sessionMap.containsKey(sessionKey)) {
sessionMap.remove(sessionKey);
}
handler.transition(context, null, handler.getFlow(context, "", getFlowName()), null, "");
return getFlowName();
}
And start flow page in the next way:
<p:commandButton value="Start Flow"
action="#{controller.initFlow}"/>
</p:panelGrid>

Expire specific managed bean instance after time interval

I have 2 JSF managed beans A and B and I need to expire/destruct/destroy A after 2 minutes and B after 5 minutes. I checked this related question Timing out from a bean, but it is expiring whole session. I do not want to expire whole session.
How can I achieve this with a custom scope?
Given that you're using JSF bean management facility (and thus not CDI, which would require a completely different answer), you can achieve this with #CustomScoped. The #CustomScoped value must refer a Map implementation in a broader, usually existing, scope.
Something like:
#ManagedBean
#CustomScoped("#{timeoutScope}")
public class TimeoutBean {}
As the #CustomScoped annotation doesn't support passing additional arguments, setting the timeout can only be done via an additional custom annotation like below:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Timeout {
/** Minutes. */
int value();
}
#ManagedBean
#CustomScoped("#{timeoutScope}")
#Timeout(5) // Expires after 5 minutes.
public class TimeoutBean {}
Now, here's a kickoff example of how the #{timeoutScope} looks like, including #PostConstruct support (automatically) and #PreDestroy support (manually):
#ManagedBean
#SessionScoped
public class TimeoutScope extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
#Override
public Object put(String name, Object bean) {
Timeout timeout = bean.getClass().getAnnotation(Timeout.class);
if (timeout == null) {
throw new IllegalArgumentException("#Timeout annotation is required on bean " + name);
}
Long endtime = System.nanoTime() + (timeout.value() * (long) 6e10);
Object[] beanAndEndtime = new Object[] { bean, endtime };
return super.put(name, beanAndEndtime);
}
#Override
public Object get(Object key) {
Object[] beanAndEndtime = (Object[]) super.get(key);
if (beanAndEndtime == null) {
return null;
}
Object bean = beanAndEndtime[0];
Long endtime = (Long) beanAndEndtime[1];
if (System.nanoTime() > endtime) {
String name = (String) key;
ScopeContext scope = new ScopeContext("timeoutScope", Collections.singletonMap(name, bean));
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().publishEvent(context, PreDestroyCustomScopeEvent.class, scope);
return null;
}
return bean;
}
}
You see, it's session scoped and implements Map. As to the scope, this way it's tied to a specific user session, not to the whole application. If you actually want to share the bean across all user sessions in the application, then make it application scoped instead. As to the Map, henever JSF needs to find a managed bean, it first tries get(). If it returns null (i.e. bean doesn't exist yet), then it will auto-create the managed bean instance and perform a put().
Inside the put(), it's a matter of extracting and calculating the timeout and store it in the map. Inside the get(), you just check the timeout and return null to indicate JSF that bean doesn't exist anymore. JSF will then simply auto-create it and come back at put(), etc.
Do note that I'm using System#nanoTime() instead of System#currentTimeMillis() as the latter is tied to OS (operating system) time, not to hardware time (and it's thus sensitive to a.o. DST and enduser-controlled changes in time).

Retrieving selectOneMenu complex object as selected item

I'm beginning with JSF (Mojarra 2.2 and Glassfish 4) and currently practicing with a web application which job is to store Clients and their Orders in DB.
When creating a new Order, one feature is to allow choosing an existing client from a JSF <h:selectOneMenu>. An Order entity stores a Client entity among other attributes...
I've followed BalusC's great answer about prepopulating a <h:selectOneMenu> from a DB (here), and have successfully populated mine from data stored in an eager ApplicationScoped ManagedBean, but I can't manage to retrieve the selected item in the backing bean as complex object. It is always null.
This is driving me mad and your help will be truly appreciated!
Here are the relevant code snippets:
#ManagedBean(eager = true)
#ApplicationScoped
public class Data implements Serializable {
private static final long serialVersionUID = 1L;
#EJB
private ClientDao clientDao;
private List<Client> clients;
#PostConstruct
private void init() {
clients = clientDao.lister();
}
public List<Client> getClients() {
return clients;
}
}
Order creation bean (note: 'commande' means order ;)
#ManagedBean
#RequestScoped
public class CreerCommandeBean implements Serializable {
private static final long serialVersionUID = 1L;
private Commande commande;
private String choixNouveauClient = "nouveauClient";
#EJB
private CommandeDao commandeDao;
public CreerCommandeBean() {
commande = new Commande();
}
public void inscrire() {
System.out.println("client : " + commande.getClient()); // prints **NULL**
// ... orderService to store in DB
}
... getters and setters
Client converter:
#FacesConverter(value = "clientConverter", forClass = Client.class)
public class ClientConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null) {
return null;
}
Data data = context.getApplication().evaluateExpressionGet(context, "#{data}", Data.class);
for (Client c : data.getClients()) {
if (c.getId().toString().equals(value)) {
return c;
}
}
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Client", value)));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value instanceof Client) ? String.valueOf(((Client) value).getId()) : null;
}
}
Facelet excerpt:
<p:outputPanel id="gridContainerAncienClient">
<p:selectOneMenu value="#{creerCommandeBean.commande.client}"
rendered="#{creerCommandeBean.choixNouveauClient == 'ancienClient'}">
<f:converter converterId="clientConverter" />
<f:selectItems value="#{data.clients}" var="cli"
itemValue="#{cli}" itemLabel="#{cli.prenom} #{cli.nom}" />
</p:selectOneMenu>
</p:outputPanel>
CreerCommandeBean is #RequestScoped. That means it will live only for one request.
When you select a client to be assigned to #{creerCommandeBean.commande.client} you do this by a request. #{creerCommandeBean.commande.client} is now the selected client. Then the request is over, the bean gets destroyed and your "changes" are lost.
When you try to retrieve that data, you do that by a request again: A new instance of CreerCommandeBean is created and the constructor assigns the property commande with a new instance of Commande whose property client again is probably null.
Solution:
Use a broader scope. e.g. #ViewScoped which makes the bean "live" as long as you stay in the same view - no matter how many requests you make.
Tip:
Read BalusC's Post on Communication is JSF 2.0. Parts might be slightly different in JSF 2.2 but it's still a good and comprehensive introduction.
I got stuck with similar problem, only to realize that I forgot to implement equals() and hashCode() method in my Object. Client Class in this case.
I should blame myself for skipping the instructions in BalusC's blog.
"...Please note the Object#equals() implementation. This is very important for JSF. After conversion, it will compare the selected item against the items in the list. As the Object#equals() also require Object#hashCode(), this is implemented as well...."

iceface 2.0: How to retain value in icepush?

I would like to integrate ice push into my web application.
I have a page(placeinfo.jsf) shows information of a place and ui:include weather.jsf for weather information of the place.
Users access to the page at http://xxx.com/xxx/placeinfo.jsf?place=california.
Simple example of code,
placeinfo.jsf
1) ice:output value="placeInfoBean.population"
2) ice:output value="placeInfoBean.language"
3) ui:include src="./weather.xhtml"
weather.jsf
1) ice:output value="weatherBean.humidity"
2) ice:output value="weatherBean.visibility"
PlaceInfoBean.java
#ManagedBean(name="placeInfoBean")
#RequestScoped
public class PlaceInfoBean
{
String population;
String language;
#PostConstruct
public void init()
{
HttpServletRequest request= (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String place = request.getParameter("place");
setPopulation(PlaceInfoDao.getPopulation(place));
setLanguage(PlaceInfoDao.getlanguage(place));
}
}
WeatherBean.java
#ManagedBean(name="weatherBean")
#RequestScoped
public class WeatherBean
{
String humidity;
String visibility;
#PostConstruct
public void init()
{
HttpServletRequest request= (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String place = request.getParameter("place");
setHumidity(WeatherDao.getHumidity(place));
setVisibility(WeatherDao.getVisibility(place));
}
public WeatherBean()
{
PushRenderer.addCurrentSession("weather");
}
}
I have another page to update the weather, and the method calls
PushRenderer.render("weather");
WeatherBean actually did a refresh, postconstrust method run again, but found there is no request param of "place" which suppose to be "california", and the page doesn't work properly then.
Question:
1) May I know besides session, how can the page remembers the value before PushRenderer did something?
2) Is it a proper way to get the request param for WeatherBean?
or request param should be passed by ui:param from placeInfo.jsf?
How to get the value of ui:param in WeatherBean?
Thank you!
Have you tried to set the managed beans as #SessionScoped? Or #ApplicationScoped? Did it work?

Iceface 2.0 commandLink partial submit doesn't work

I have a page which takes in request params for place, then generate information,
for example, http://example.com/xxx/weather.jsf?place=california.
The purpose of doing this is to let user bookmark the link.
In the weather.jsf, there are two outputtext and a commandlink:
Humidity : <ice:outputText value="#{weatherBean.humidity}"/>
Visibility : <ice:outputText value="#{weatherBean.visibility}"/>
<ice:commandLink id="likeButton"
value="Like"
actionListener="#{weatherBean.doLike}" />
In the managedBean:
#ManagedBean(name="weatherBean")
#RequestScoped
public class WeatherBean
{
String humidity;
String visibility;
int numLike;
#PostConstruct
public void init()
{
System.out.println("init called");
HttpServletRequest request= (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String place = request.getParameter("place");
setHumidity(WeatherDao.getHumidity(place));
setVisibility(WeatherDao.getVisibility(place));
setNumLike(GeneralDao.getNumLike());
}
public void doLike(ActionEvent event)
{
System.out.println("doLike called");
GeneralDao.addNumberLike();
}
}
Alright, the page generated perfectly.
However, when I click the doLike commandLink,
it always triggers the init method first, then call doLike method.
Since the request param is empty, all the other values reset.
Is there any way to prevent a refresh of the page or calling of init method?
I tried partialsubmit or immediate, but no luck.
Your bean is #RequestScoped, so after executing the JSF lifecycle, your bean instance is lost, until the next request comes in, at which point you get a new instance of your bean, and the PostContruct re-executes.
Try changing the scope of your bean to something longer lived, like #ViewScoped.

Resources