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

        Philip Breau created issue -
        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); } } }
        Arran Mccullough made changes -
        Field Original Value New Value
        Salesforce Case Reference 5007000000W6tj3AAB
        Ken Fyten made changes -
        Assignee Mircea Toma [ mircea.toma ]
        Fix Version/s 3.4 [ 10770 ]
        Fix Version/s EE-3.4.0.GA [ 11171 ]
        Assignee Priority P1 [ 10010 ]
        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.
        Mircea Toma made changes -
        Status Open [ 1 ] Resolved [ 5 ]
        Resolution Fixed [ 1 ]
        Repository Revision Date User Message
        ICEsoft Public SVN Repository #38294 Thu Sep 26 13:46:59 MDT 2013 mircea.toma ICE-9606 Provide the FacesContext instance to the @PostConstruct, @PreDestroy and @AllWindowsClosed
        Files Changed
        Commit graph MODIFY /icefaces3/trunk/icefaces/core/src/main/java/org/icefaces/impl/application/WindowScopeManager.java
        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.
        Mircea Toma made changes -
        Resolution Fixed [ 1 ]
        Status Resolved [ 5 ] Reopened [ 4 ]
        Ken Fyten made changes -
        Affects Documentation (User Guide, Ref. Guide, etc.) [ 10003 ]
        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 .
        Mircea Toma made changes -
        Status Reopened [ 4 ] Resolved [ 5 ]
        Resolution Fixed [ 1 ]
        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
        Philip Breau made changes -
        Resolution Fixed [ 1 ]
        Status Resolved [ 5 ] Reopened [ 4 ]
        Philip Breau made changes -
        Assignee Mircea Toma [ mircea.toma ] Philip Breau [ philip.breau ]
        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
        Philip Breau made changes -
        Status Reopened [ 4 ] Resolved [ 5 ]
        Resolution Won't Fix [ 2 ]
        Philip Breau made changes -
        Fix Version/s 3.4 [ 10770 ]
        Fix Version/s EE-3.4.0.GA [ 11171 ]
        Ken Fyten made changes -
        Status Resolved [ 5 ] Closed [ 6 ]

          People

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

            Dates

            • Created:
              Updated:
              Resolved: