3

My goal is to keep session size as small as possible. (Why?.. it's other topic). What I have is Phase listener declared in faces-config.xml

<lifecycle>
        <phase-listener>mypackage.listener.PhaseListener</phase-listener>   
</lifecycle>

I want to save all other views, except the last one(maximum two) , in some memcache. Getting the session map:

Map<String, Object> sessionMap = event.getFacesContext().getExternalContext().getSessionMap();

in beforePhase(PhaseEvent event) method is giving me access to all views. So here I could save all views to the memcache and delete them from the session. The question is where in jsf these views that are still loaded in the browser are requested so that I can refill with this view if it's needed. Is it possible at all? Thank you.

makkasi
  • 6,328
  • 4
  • 45
  • 60
  • I'm using google app engine. In the web I couldn't find anything readable that is for saving session in the google app engine memcache so that is why I'm trying doing this via the jsf just saving the views in the memcache instead of full session management in the memcache which I think it's risky taking the fact that memcache can be cleared every moment. I would update my question tags , because of your answer that is not possible to do this only using the jsf lifecycle. – makkasi Nov 24 '14 at 10:22

1 Answers1

4

To address the core of your question, implement a ViewHandler, within which you can take control of the RESTORE_VIEW and RENDER_RESPONSE phases/processes. You'll save the view during the RENDER_RESPONSE and selectively restore, during the RESTORE_VIEW phase. Your view handler could look something like the following

   public class CustomViewHandlerImpl extends ViewHandlerWrapper{

        @Inject ViewStore viewStore; //hypothetical storage for the views. Could be anything, like a ConcurrentHashMap
        ViewHandler wrapped;

        public CustomViewHandlerImpl(ViewHandler toWrap){          
           this.wrapped = toWrap;
        }

        public UIViewRoot restoreView(FacesContext context, String viewId) throws IOException{

            //this assumes you've previously saved the view, using the viewId

            UIViewRoot theView = viewStore.get(viewId);

            if(theView == null){

               theView = getWrapped().restoreView(context, viewId);              
            }

           return theView;       
        } 


      public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException{

          viewStore.put(viewToRender.getId(),viewToRender);

          getWrapped().renderView(context, viewToRender);

      }

   }

Simply plug in your custom viewhandler, using

<view-handler>com.you.customs.CustomViewHandlerImpl</view-handler>

Of course, you probably don't want to give this treatment to all your views; you're free to add any conditions to the logic above, to implement conditional view-saving and restoration.


You should also consider other options. It appears that you're conflating issues here. If your true concern is limit the overhead associated with view processing, you should consider

  1. Stateless Views, new with JSF-2.2. The stateless view option allows you to exclude specific pages from the JSF view-saving mechanism, simply by specifying transient="true" on the f:view. Much cleaner than mangling the UIViewRoot by hand. The caveat here is that a stateless view cannot be backed by scopes that depend on state-saving, i.e. @ViewScoped. In a stateless view, the @ViewScoped bean is going to be recreated for every postback. Ajax functionality also suffers in this scenario, because state saving is the backbone of ajax-operations.

  2. Selectively set mark components as transient The transient property is available for all UIComponents, which means, on a per-view basis, you can mark specific components with transient="true", effectively giving you the same benefits as 1) but on a much smaller scope. Without the downside of no ViewScoped

EDIT: For some reason, UIViewRoot#getViewId() is not returning the name of the current view (this might be a bug). Alternatively, you can use

ExternalContext extCtxt = FacesContext.getCurrentInstance().getExternalContext();
String viewName = ((HttpServletRequest)extCtxt.getRequest()).getRequestURI();  //use this id as the key to store your views instead
kolossus
  • 20,559
  • 3
  • 52
  • 104
  • Unfortunately I'm not familiar with stateless views. Currently I'm reading CDI with JSF 2.2 and WELD.Need to educate myself more. The problem comes from jsf not clearing its ViewScoped beans when using bean action method. (although I read that this bug is out in jsf 2.2) So when I click on the same tab few times , then multiple viewscoped beans are saved in the session. But session in GAE cannot be more then 1MB.My options: using scope that is managed properly or managing the views myself. Now I'm trying to make my custom viewscope as BalucS suggested. I don't know why he deleted his comments. – makkasi Nov 25 '14 at 15:23
  • 1
    There is a more robust way to manage your viewscope, using a custom viewhandler @makkasi. Setting a scope as stateless is a very trivial change. Actively managing the viewscope however is a bit more involved. I'm still fleshing out my answer – kolossus Nov 25 '14 at 15:30
  • I will try what you suggested on first place and hope it works. Using stateless looks like is not option for me , if ViewScope is not working with stateless , because is my preferred scope. Thank you – makkasi Nov 25 '14 at 15:46
  • Hi @kolossus After few months I'm back on this problem. I'm trying your solution but what I receive as viewToRender.getId() is 'j_id1' , and I save the view under this key, but in restoreView the viewId is '/login.xhtml'. Why is that and how to have same ID for put and get. – makkasi Mar 13 '15 at 11:58
  • That's quite surprising @makkasi. See my edit. Also, why did it take so long to report your issue? Was it working at some point and then it stopped? – kolossus Mar 13 '15 at 14:10
  • No. I've never tried that until today. It took so long because I had started other project and hadn't more time to lose , because I'd loosed one week for solution if I remember. Sorry to disturb you writing comment to you. – makkasi Mar 13 '15 at 14:16
  • Not a problem @makkasi – kolossus Mar 13 '15 at 14:43