ICEfaces
  1. ICEfaces
  2. ICE-9606

AllWindowsClosed handlers have no access to FacesContext

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Major Major
    • Resolution: Won't Fix
    • Affects Version/s: EE-3.3.0.GA_P01
    • Fix Version/s: None
    • Component/s: Framework
    • Labels:
      None
    • Environment:
      n/a

      Description

      The @AllWindowsClosed annotation allows users to have a method called when all session windows and tabs have been closed, in order clear resources, trigger actions, etc.

      The callback method, however, currently can't have any context. As it's called within another thread, it can't access the FacesContext directly. It would be useful if the AllWindowsClosedNotifier passed this in through the method parameter, if the method parameter exists.

        Activity

        Hide
        Philip Breau added a comment - - edited
        Index: C:/work/projects/icefaces3/icefaces/core/src/main/java/org/icefaces/impl/application/WindowScopeManager.java
        ===================================================================
        --- C:/work/projects/icefaces3/icefaces/core/src/main/java/org/icefaces/impl/application/WindowScopeManager.java	(revision 38267)
        +++ C:/work/projects/icefaces3/icefaces/core/src/main/java/org/icefaces/impl/application/WindowScopeManager.java	(working copy)
        @@ -16,6 +16,7 @@
         
         package org.icefaces.impl.application;
         
        +
         import org.icefaces.bean.AllWindowsClosed;
         import org.icefaces.bean.WindowDisposed;
         import org.icefaces.impl.push.SessionViewManager;
        @@ -233,7 +234,7 @@
                     long windowScopeExpiration = EnvUtils.getWindowScopeExpiration(context);
                     //copy session beans before they're cleared on FacesContext.release()
                     final Map session = new HashMap(context.getExternalContext().getSessionMap());
        -            timer.schedule(new AllWindowsClosedNotifier(state, session), windowScopeExpiration * 2);
        +            timer.schedule(new AllWindowsClosedNotifier(state, session, context), windowScopeExpiration * 2);
                 }
         
                 disposeViewScopeBeans(context);
        @@ -263,8 +264,8 @@
                 }
                 facesContext.setExceptionHandler(oldHandler);
             }
        -
        -    private static void callAnnotatedMethod(Object object, Class annotation) {
        +    
        +    private static void callAnnotatedMethodWithContext(Object object, Class annotation, FacesContext context) {
                 Class theClass = object.getClass();
                 try {
                     while (null != theClass) {
        @@ -272,7 +273,25 @@
                         for (Method method : methods) {
                             if (method.isAnnotationPresent(annotation)) {
                                 method.setAccessible(true);
        -                        method.invoke(object);
        +                        if( context != null ){
        +                            List<Class<?>> methodParams = Arrays.asList(method.getParameterTypes());
        +                            int index = methodParams.indexOf(FacesContext.class);
        +                            if( index == 0 ){
        +                                method.invoke(object);
        +                            }
        +                            else{
        +                                method.invoke(object, context);
        +                            }
        +                            if( index > 0 ){
        +                                log.log(Level.WARNING, "Failed to invoke" + annotation + " on " + theClass 
        +                                        + ". If expecting a FacesContext parameter, it must be the first and only parameter");
        +                            }
        +                        }
        +                        else{
        +                            method.invoke(object, context);
        +                        }
        +                        
                                 return;
                             }
                         }
        @@ -283,6 +302,10 @@
                 }
             }
         
        +    private static void callAnnotatedMethod(Object object, Class annotation) {
        +        callAnnotatedMethodWithContext(object, annotation, null);
        +    }
        +
             public static void sessionCreated(HttpSession session) {
                 session.setAttribute(SessionSynchronizationMonitor, new SynchronizationMonitorObject());
             }
        @@ -610,10 +633,13 @@
             private static class AllWindowsClosedNotifier extends TimerTask {
                 private final State state;
                 private final Map session;
        +        private final FacesContext context;
         
        -        public AllWindowsClosedNotifier(State state, Map session) {
        +        public AllWindowsClosedNotifier(State state, Map session, FacesContext context) {
                     this.state = state;
                     this.session = session;
        +            this.context = context;
                 }
         
                 public void run() {
        @@ -634,7 +660,7 @@
                         Iterator objects = session.values().iterator();
                         while (objects.hasNext()) {
                             Object object = objects.next();
        -                    callAnnotatedMethod(object, AllWindowsClosed.class);
        +                    callAnnotatedMethodWithContext(object, AllWindowsClosed.class, context);
                         }
                     }
                 }
        
        Show
        Philip Breau added a comment - - edited Index: C:/work/projects/icefaces3/icefaces/core/src/main/java/org/icefaces/impl/application/WindowScopeManager.java =================================================================== --- C:/work/projects/icefaces3/icefaces/core/src/main/java/org/icefaces/impl/application/WindowScopeManager.java (revision 38267) +++ C:/work/projects/icefaces3/icefaces/core/src/main/java/org/icefaces/impl/application/WindowScopeManager.java (working copy) @@ -16,6 +16,7 @@ package org.icefaces.impl.application; + import org.icefaces.bean.AllWindowsClosed; import org.icefaces.bean.WindowDisposed; import org.icefaces.impl.push.SessionViewManager; @@ -233,7 +234,7 @@ long windowScopeExpiration = EnvUtils.getWindowScopeExpiration(context); //copy session beans before they're cleared on FacesContext.release() final Map session = new HashMap(context.getExternalContext().getSessionMap()); - timer.schedule( new AllWindowsClosedNotifier(state, session), windowScopeExpiration * 2); + timer.schedule( new AllWindowsClosedNotifier(state, session, context), windowScopeExpiration * 2); } disposeViewScopeBeans(context); @@ -263,8 +264,8 @@ } facesContext.setExceptionHandler(oldHandler); } - - private static void callAnnotatedMethod( Object object, Class annotation) { + + private static void callAnnotatedMethodWithContext( Object object, Class annotation, FacesContext context) { Class theClass = object.getClass(); try { while ( null != theClass) { @@ -272,7 +273,25 @@ for (Method method : methods) { if (method.isAnnotationPresent(annotation)) { method.setAccessible( true ); - method.invoke(object); + if ( context != null ){ + List< Class <?>> methodParams = Arrays.asList(method.getParameterTypes()); + int index = methodParams.indexOf(FacesContext.class); + if ( index == 0 ){ + method.invoke(object); + } + else { + method.invoke(object, context); + } + if ( index > 0 ){ + log.log(Level.WARNING, "Failed to invoke" + annotation + " on " + theClass + + ". If expecting a FacesContext parameter, it must be the first and only parameter" ); + } + } + else { + method.invoke(object, context); + } + return ; } } @@ -283,6 +302,10 @@ } } + private static void callAnnotatedMethod( Object object, Class annotation) { + callAnnotatedMethodWithContext(object, annotation, null ); + } + public static void sessionCreated(HttpSession session) { session.setAttribute(SessionSynchronizationMonitor, new SynchronizationMonitorObject()); } @@ -610,10 +633,13 @@ private static class AllWindowsClosedNotifier extends TimerTask { private final State state; private final Map session; + private final FacesContext context; - public AllWindowsClosedNotifier(State state, Map session) { + public AllWindowsClosedNotifier(State state, Map session, FacesContext context) { this .state = state; this .session = session; + this .context = context; } public void run() { @@ -634,7 +660,7 @@ Iterator objects = session.values().iterator(); while (objects.hasNext()) { Object object = objects.next(); - callAnnotatedMethod(object, AllWindowsClosed.class); + callAnnotatedMethodWithContext(object, AllWindowsClosed.class, context); } } }
        Hide
        Mircea Toma added a comment -

        Applied Philip's patch with slight modifications.
        Now methods annotated with @PostConstruct, @PreDestroy and @AllWindowsClosed will receive the FacesContext instance if the method defines the parameter.

        Show
        Mircea Toma added a comment - Applied Philip's patch with slight modifications. Now methods annotated with @PostConstruct, @PreDestroy and @AllWindowsClosed will receive the FacesContext instance if the method defines the parameter.
        Hide
        Mircea Toma added a comment - - edited

        Need to update documentation for @AllWindowsClosed annotation, and probably for @PreDestroy and @PostConstruct.

        Show
        Mircea Toma added a comment - - edited Need to update documentation for @AllWindowsClosed annotation, and probably for @PreDestroy and @PostConstruct.
        Hide
        Mircea Toma added a comment -

        Added documentation for this new feature, see http://www.icesoft.org/wiki/display/ICE/AllWindowsClosed .

        Show
        Mircea Toma added a comment - Added documentation for this new feature, see http://www.icesoft.org/wiki/display/ICE/AllWindowsClosed .
        Hide
        Philip Breau added a comment - - edited

        Unfortunately the FacesContext, not being set on the current ThreadLocalVariable, is causing issues, as attempting to access the ExternalContext runs into the assertNotReleased() check. In this scenario, the user is trying to access the Session through the FacesContext in order to remove a session variable. Is it unsafe to set the FacesContext on the new Thread? If so, perhaps we could also allow injecting the ExternalContext or Session object?

        WARNING: Failed to invokeinterface org.icefaces.bean.AllWindowsClosed on class com.acme.bean.web.UserInfoB

        ean

        java.lang.reflect.InvocationTargetException

        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

        at java.lang.reflect.Method.invoke(Method.java:597)

        at org.icefaces.impl.application.WindowScopeManager.callAnnotatedMethod(WindowScopeManager.java:282)

        at org.icefaces.impl.application.WindowScopeManager.access$1900(WindowScopeManager.java:42)

        at org.icefaces.impl.application.WindowScopeManager$AllWindowsClosedNotifier.run(WindowScopeManager.java:655)

        at java.util.TimerThread.mainLoop(Timer.java:512)

        at java.util.TimerThread.run(Timer.java:462)

        Caused by: java.lang.IllegalStateException

        at com.sun.faces.context.FacesContextImpl.assertNotReleased(FacesContextImpl.java:654)

        at com.sun.faces.context.FacesContextImpl.getExternalContext(FacesContextImpl.java:139)

        at org.icefaces.impl.context.ICEfacesContext.getExternalContext(ICEfacesContext.java:25)

        at com.acme.bean.web.UserInfoBean.allWindowsForSessionClosed(UserInfoBean.java:204)

        ... 9 more

        Show
        Philip Breau added a comment - - edited Unfortunately the FacesContext, not being set on the current ThreadLocalVariable, is causing issues, as attempting to access the ExternalContext runs into the assertNotReleased() check. In this scenario, the user is trying to access the Session through the FacesContext in order to remove a session variable. Is it unsafe to set the FacesContext on the new Thread? If so, perhaps we could also allow injecting the ExternalContext or Session object? WARNING: Failed to invokeinterface org.icefaces.bean.AllWindowsClosed on class com.acme.bean.web.UserInfoB ean java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.icefaces.impl.application.WindowScopeManager.callAnnotatedMethod(WindowScopeManager.java:282) at org.icefaces.impl.application.WindowScopeManager.access$1900(WindowScopeManager.java:42) at org.icefaces.impl.application.WindowScopeManager$AllWindowsClosedNotifier.run(WindowScopeManager.java:655) at java.util.TimerThread.mainLoop(Timer.java:512) at java.util.TimerThread.run(Timer.java:462) Caused by: java.lang.IllegalStateException at com.sun.faces.context.FacesContextImpl.assertNotReleased(FacesContextImpl.java:654) at com.sun.faces.context.FacesContextImpl.getExternalContext(FacesContextImpl.java:139) at org.icefaces.impl.context.ICEfacesContext.getExternalContext(ICEfacesContext.java:25) at com.acme.bean.web.UserInfoBean.allWindowsForSessionClosed(UserInfoBean.java:204) ... 9 more
        Hide
        Philip Breau added a comment -

        Reverted injecting the FacesContext due to access restrictions on the ExternalContext. The workaround is to simply reference the necessary resource, such as the Session, etc. in a bean instance variable in a JSF thread, which will then be accessible to the AllWindowsClosed method handler outside of a JSF thread.

        Wiki changes reverted.

        Revision: 38586
        Author: philip.breau
        Date: October-11-13 3:00:12 PM
        Message:
        reverting Revision: 38294
        Author: mircea.toma
        Date: September-26-13 4:46:59 PM
        as FacesContext must be set on the current ThreadLocalVariable for the ExternalContext to be used


        Modified : /icefaces3/trunk/icefaces/core/src/main/java/org/icefaces/impl/application/WindowScopeManager.java

        Show
        Philip Breau added a comment - Reverted injecting the FacesContext due to access restrictions on the ExternalContext. The workaround is to simply reference the necessary resource, such as the Session, etc. in a bean instance variable in a JSF thread, which will then be accessible to the AllWindowsClosed method handler outside of a JSF thread. Wiki changes reverted. Revision: 38586 Author: philip.breau Date: October-11-13 3:00:12 PM Message: reverting Revision: 38294 Author: mircea.toma Date: September-26-13 4:46:59 PM as FacesContext must be set on the current ThreadLocalVariable for the ExternalContext to be used Modified : /icefaces3/trunk/icefaces/core/src/main/java/org/icefaces/impl/application/WindowScopeManager.java

          People

          • Assignee:
            Philip Breau
            Reporter:
            Philip Breau
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: