1

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(); 
        ...

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
russellelbert
  • 643
  • 4
  • 9
  • 23
  • 1
    Are you sure that a servlet filter is in first place the right tool for the job you had in mind? https://xyproblem.info – BalusC Nov 18 '21 at 15:55
  • @BalusC Currently in the filter I'm using ThreadContext to add key/values for log4j2, e.g. `ThreadContext.put("sessionId", session.getId());` and was hoping for a simple similar solution for a backing bean ID. I'm open to other ways, but unsure how to approach it otherwise. – russellelbert Nov 18 '21 at 16:01
  • I think you could use a phase listener, you have an explanation about how to implement it by @BalusC in this other answer https://stackoverflow.com/questions/8388854/how-to-implement-a-phaselistener-which-runs-at-end-of-lifecycle – gmanjon Nov 19 '21 at 07:03
  • 1
    Ah. The `javax.faces.ViewState` request param might be sufficient if you're using server side state saving. – BalusC Nov 19 '21 at 10:57

2 Answers2

0

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 {
}

gmanjon
  • 1,483
  • 1
  • 12
  • 16
0

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);
        ...
russellelbert
  • 643
  • 4
  • 9
  • 23