Unique ID for a JSF ViewScoped bean across all ajax requests? - jsf

I'm working (mostly) with #ViewScoped #Named Faces beans (javax.faces.view.ViewScoped and javax.inject.Named) and would like to know if from the ServletRequest (or HttpServletRequest) I can differentiate between two different instantiations of the same bean within a javax.servlet.Filter
For example, a user opens the same page in two different tabs, but both of these have the same SessionID via the call httpRequest.getSession().getId(). Currently this is not helpful to me.
Alternatively I have access to the Thread ID, but this changes with each ajax call, and I need to keep around something unique for just the instance of the backing bean across all calls.. until the bean is destroyed.
So, something like this (this doesn't work, but hopefully illustrates my need)
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
try
{
HttpServletRequest httpRequest = (HttpServletRequest) request;
// not real code, but this is what I'm looking for;
// get a unique identifier for just this instance of the bean that stays the same across all ajax requests
String beanId = request.getCurrentBackingBean.getInstanceId();
...

You can acquire it via FacesContext, but not on a ServletFilter.ServletFilter's run before getting to the Servlet, which JSF is, so no way to access FacesContext until you reach de Servlet (your entry point would be any managed bean).
But, if you manage to reshape you business logic to be able to do what you want inside a managed bean, you can get the view id this way:
FacesContext.getCurrentInstance().getViewRoot().getViewId();
Or if you are in JSF 2.3 you can just inject FacesContext in any managed bean:
#Inject
FacesContext facesContext;
...
public void anyMethod() {
facesContext.getViewRoot().getViewId();
}
In order to the injection of FacesContext to work, you have to activate JSF version 2.3 specific features via a special annotation in a CDI Bean for instance:
import javax.enterprise.context.ApplicationScoped;
import javax.faces.annotation.FacesConfig;
#FacesConfig(version = FacesConfig.Version.JSF_2_3)
#ApplicationScoped
public class JSF23ActivationBean {
}

As #BalusC mentioned in a comment, a servlet filter may not be the most optimal place to solve this, but I'm working with existing code that may not allow refactoring.
His other comments have led me to this solution:
In the Filter: using the HttpServletRequest, get the parameter "javax.faces.ViewState". This contains a unique id for the view of my (ViewScoped) backing bean that lives across Ajax requests. It will create a new unique id for each new instantiation of the backing bean.
Note that it does not seem to be available in the first call to the filter, I assume because the #PostConstruct has not yet been called on the new backing bean. My initial attempts to solve this was to pull it out of the FacesContext (in the #PostConstruct) with the calls:
String viewState = FacesContext.getCurrentInstance().getApplication().getStateManager().getViewState(FacesContext.getCurrentInstance());
ThreadContext.put("viewState", viewState);
and although this appears to get the viewState, it seems to break up the lifecycle somehow, and my page does not render. I wonder if it is not possible to 'grab' the viewState until after the page is rendered?
For now, the working code in the Filter now looks like this (truncated):
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
try
{
HttpServletRequest httpRequest = (HttpServletRequest) request;
String viewState = httpRequest.getParameter("javax.faces.ViewState");
...
ThreadContext.put("viewState", viewState);
...

Related

SEAM redirect page from backing bean

I have a proper newbie SEAM question, I want to redirect the user to a different page from a backing bean
I know in most cases you should use pages.xml however there could be a number of different pages depending on the bean logic so it seems like it should be a lot easier to do from the bean.
I cant see any examples of people doing this so Im guessing there is a reason why, Maybe something like this would work??...
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
((HttpServletRequest) ec.redirect("http://example.com/");
You can use the Redirect component:
#Name("yourBean")
public class YourBean {
#In
Redirect redirect;
public void yourMethod() {
redirect.setViewId("/someView.xhtml");
redirect.setParameter("someParam", "someValue");
redirect.execute();
}
}
Or going with FacesManager:
FacesManager.instance().redirect("/someView.xhtml", paramMap,
conversationPropagationEnabled, includePageParams);
These only work for other JSF views (ie .xhtml). If you just want an arbitrary URL, you can use the FacesContext as you mentioned in your question.

Get ManagedBeans in Custom JSF Validator dynamically

I want to validate values dynamically by using a custom validator that I can be used with several components. The custom validator gets those values from different ManagedBeans depending on its usage.
How to determine which ManagedBean the value is retrieved from? So I can get it, validate it, and put it back into a ManagedBean.
My Custom Validator:
#FacesValidator(value = "valid")
public class DateValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
// Bean bean = ?
}
}
I dont think that is good approach as in validation phase model is still not updated, what you are getting is just input value from view. I wonder why you need bean instance there. If your validation depends on other component in view you can refer link # SO and Validator for multiple fields.
As you refer you need to validate it and put back, thats why validation phase is all about, validate it in your validator and if it fails it will not be passed to managed bean.
Have a look at this link for JSF lifecycle JSF Lifecycle
Hope this helps !!!

Refresh managed session bean in JSF 2.0

After I commit some data into the database I want my session beans to automatically refresh themselves to reflect the recently committed data. How do I achieve this when using managed session beans in JSF 2.0?
Currently I have to restart the web server in order for the sessions to clear and load anew again.
2 ways:
Put them in the view scope instead. Storing view-specific data sessionwide is a waste. If you have a performance concern, you should concentrate on implementing connection pooling, DB-level pagination and/or caching in the persistence layer (JPA2 for example supports second level caching).
#ManagedBean
#ViewScoped
public class FooBean {
// ...
}
Add a public load() method so that it can be invoked from the action method (if necessary, from another bean).
#ManagedBean
#SessionScoped
public class FooBean {
private List<Foo> foos;
#EJB
private FooService fooService;
#PostConstruct
public void load() {
foos = fooService.list();
}
// ...
}
which can be invoked in action method inside the same bean (if you submit the form to the very same managed bean of course):
public void submit() {
fooService.save(foos);
load();
}
or from an action method in another bean (for the case that your managed bean design is a bit off from usual):
#ManagedProperty("#{fooBean}")
private FooBean fooBean;
public void submit() {
fooService.save(foos);
fooBean.load();
}
This of course only affects the current session. If you'd like to affect other sessions as well, you should really consider putting them in the view scope instead, as suggested in the 1st way.
See also:
How to choose the right bean scope?

#postConstruct in JSF 1.1

How do I simulate the #postConstruct behaviour in JSF 1.1 like as in JSF 1.2 and newer?
Actually, I want to call a bean method automatically during page loading?
I am using IceFaces 1.8 on JSF 1.1.
The point of #PostConstruct is to provide a hook to execute some code after all managed properties (as in <managed-property> or #ManagedProperty) are been set and all dependency injections (e.g. #EJB, #Resource, #Inject and so on) have taken place.
If you don't have any of them, just use the bean's constructor.
public class Bean {
public Bean() {
// Just do your job here. Don't do it the hard way.
}
// ...
}
Or if you actually want to execute it when a specific property has been set, then do the job in the setter while null-checking the current property value.
public class Bean {
private SomeObject someManagedProperty;
public void setSomeManagedProperty(someManagedProperty) {
if (this.someManagedProperty == null && someManagedProperty != null) {
// First-time set, now you can do your job here.
}
this.someManagedProperty = someManagedProperty;
}
// ...
}
Update as per the comments:
I meant to execute the method every time the page is loaded
The #PostConstruct doesn't do that. However, if the bean is request scoped, then you will see the same effect. You seem to be using a session or application scoped managed bean to manage request scoped data. This is in essence wrong. You should convert it to a request scoped bean. Any real session scoped data can be split into a session scoped bean which you then inject by <managed-property>.

How can i send a parameter to be used in the #PostConstruct method of a backing bean?

I need to preload some data to be displayed when the page loads. The initialization steps are performed on a #PostConstruct-annotated method but now i need to use a parameter in order to get the data.
What i'm trying to do:
#PostConstruct
public void init()
{
List data = getDataFromDB(parameter) /*Need to read a parameter created somewhere else*/
}
Is there a way to achieve this?
Thanks in advance
It's kind of hard to say what you mean by "a parameter set somewhere else". I will assume that "somewhere else" means "sent from browser by HTTP". In such case you should create a standard property in your managed bean and:
in JSF 2.0 you could annotate it with #ManagedProperty("#{param.nameOfParameterToRead}")
in JSF 1.2 and less - use managed-property element in your bean description (faces-config.xml).
Like this:
#ManagedBean
#RequestScoped
class MyManagedBean {
#ManagedProperty("#{param.id}")
public Integer id;
#PostConstruct
public void init(){
data = getDataFromDB(id)
}
// setters and getters (mandatory, even though annotation is on an attribute!!!)
}
Careful: injecting properties does not use JSF converters, so it is best to inject strings and take care of conversion in your own code.
how about reading from Properties file, or fetching List from DB ??

Resources