Skip to content

Instantly share code, notes, and snippets.

@jonaskahn
Last active May 7, 2019 09:10
Show Gist options
  • Save jonaskahn/6d1db8ca44479e4544a970d480213bf9 to your computer and use it in GitHub Desktop.
Save jonaskahn/6d1db8ca44479e4544a970d480213bf9 to your computer and use it in GitHub Desktop.
Short explanation for How SystemEvent execute in JSF

How JSF Life Cycle execute SystemEvent

Look into current implementation

LifecycleImpl.java

    // Execute the phases up to but not including Render Response
    public void execute(FacesContext context) throws FacesException {

        if (context == null) {
            throw new NullPointerException
                (MessageUtils.getExceptionMessageString
                 (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"));
        }

        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("execute(" + context + ")");
        }

        for (int i = 1, len = phases.length -1 ; i < len; i++) { // Skip ANY_PHASE placeholder

            if (context.getRenderResponse() ||
                context.getResponseComplete()) {
                break;
            }

            phases[i].doPhase(context, this, listeners.listIterator());

        }

    }


    // Execute the Render Response phase
    public void render(FacesContext context) throws FacesException {

        if (context == null) {
            throw new NullPointerException
                (MessageUtils.getExceptionMessageString
                 (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"));
        }

        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("render(" + context + ")");
        }

        if (!context.getResponseComplete()) {
            response.doPhase(context, this, listeners.listIterator());
        }

    }

The RenderResponse phase will be particularly executed

response.doPhase(context, this, listeners.listIterator());

Now, we dig into function doPhase()

Phase.java

public void doPhase(FacesContext context,
                        Lifecycle lifecycle,
                        ListIterator<PhaseListener> listeners) {

        context.setCurrentPhaseId(getId());
        PhaseEvent event = null;
        if (listeners.hasNext()) {
            event = new PhaseEvent(context, this.getId(), lifecycle);
        }

        // start timing - include before and after phase processing
        Timer timer = Timer.getInstance();
        if (timer != null) {
            timer.startTiming();
        }

        try {
            handleBeforePhase(context, listeners, event);
            if (!shouldSkip(context)) {
                execute(context);
            }
        } catch (Throwable e) {
            queueException(context, e);
        } finally {
            try {
                handleAfterPhase(context, listeners, event);
            } catch (Throwable e) {
                queueException(context, e);
            }
            // stop timing
            if (timer != null) {
                timer.stopTiming();
                timer.logResult(
                      "Execution time for phase (including any PhaseListeners) -> "
                      + this.getId().toString());
            }

            context.getExceptionHandler().handle();
        }

    }

The order when a phase do the job is : beforePhase() [ handleBeforePhase(...)] -> execute() [execute(...)] -> afterPhase() [handleAfterPhase(...)]

What they did inside execute() in RenderResponse phase

RenderResponsePhase.java

public void execute(FacesContext facesContext) throws FacesException {

        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("Entering RenderResponsePhase");
        }
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("About to render view " +
                 facesContext.getViewRoot().getViewId());
        }
        // For requests intended to produce a partial response, we need prohibit
        // writing any content outside of the view itself (f:view).
        facesContext.getPartialViewContext();
        
        try {

            ViewHandler vh = facesContext.getApplication().getViewHandler();

            ViewDeclarationLanguage vdl =
                  vh.getViewDeclarationLanguage(facesContext,
                                                    facesContext.getViewRoot().getViewId());
            if (vdl != null) {
                vdl.buildView(facesContext, facesContext.getViewRoot());
            }

            boolean viewIdsUnchanged;
            do {
                String beforePublishViewId = facesContext.getViewRoot().getViewId();
                // the before render event on the view root is a special case to keep door open for navigation
                // this must be called *after* PDL.buildView() and before VH.renderView()
                facesContext.getApplication().publishEvent(facesContext,
                                                           PreRenderViewEvent.class,
                                                           facesContext.getViewRoot());
                String afterPublishViewId = facesContext.getViewRoot().getViewId();
                viewIdsUnchanged = beforePublishViewId == null && afterPublishViewId == null ||
                        (beforePublishViewId != null && afterPublishViewId != null) &&
                        beforePublishViewId.equals(afterPublishViewId);
                if (facesContext.getResponseComplete()) {
                    return;
                }
            } while (!viewIdsUnchanged);
            
            //render the view
            vh.renderView(facesContext, facesContext.getViewRoot());

        } catch (IOException e) {
            throw new FacesException(e.getMessage(), e);
        }

        if (LOGGER.isLoggable(Level.FINEST)) {
            LOGGER.log(Level.FINEST, "+=+=+=+=+=+= View structure printout for " + facesContext.getViewRoot().getViewId());
            DebugUtil.printTree(facesContext.getViewRoot(), LOGGER, Level.FINEST);
        }

        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("Exiting RenderResponsePhase");
        }

    }

We will see SystemEvents : PreRenderViewEvent will be executed.

facesContext.getApplication().publishEvent(facesContext, PreRenderViewEvent.class, facesContext.getViewRoot());

Extend

/**
     * Handle <code>afterPhase</code> <code>PhaseListener</code> events.
     * @param context the FacesContext for the current request
     * @param listenersIterator a ListIterator for the PhaseListeners that need
     *  to be invoked
     * @param event the event to pass to each of the invoked listeners
     */
    protected void handleAfterPhase(FacesContext context,
                                    ListIterator<PhaseListener> listenersIterator,
                                    PhaseEvent event) {

        try {
            Flash flash = context.getExternalContext().getFlash();
            flash.doPostPhaseActions(context);
        } catch (UnsupportedOperationException uoe) {
            if (LOGGER.isLoggable(Level.FINE)) {
                 LOGGER.fine("ExternalContext.getFlash() throw UnsupportedOperationException -> Flash unavailable");
            }    
        }
        while (listenersIterator.hasPrevious()) {
            PhaseListener listener = listenersIterator.previous();
            if (this.getId().equals(listener.getPhaseId()) ||
                PhaseId.ANY_PHASE.equals(listener.getPhaseId())) {
                try {
                    listener.afterPhase(event);
                } catch (Exception e) {
                    queueException(context,
                                   e,
                                   ExceptionQueuedEventContext.IN_AFTER_PHASE_KEY);
                    return;
                }
            }
        }

    }


     /**
     * Handle <code>beforePhase</code> <code>PhaseListener</code> events.
     * @param context the FacesContext for the current request
     * @param listenersIterator a ListIterator for the PhaseListeners that need
     *  to be invoked
     * @param event the event to pass to each of the invoked listeners
     */
     protected void handleBeforePhase(FacesContext context,
                                      ListIterator<PhaseListener> listenersIterator,
                                      PhaseEvent event) {

         try {
            Flash flash = context.getExternalContext().getFlash();
            flash.doPrePhaseActions(context);
         } catch (UnsupportedOperationException uoe) {
             if (LOGGER.isLoggable(Level.FINE)) {
                 LOGGER.fine("ExternalContext.getFlash() throw UnsupportedOperationException -> Flash unavailable");
             }
         }
         RequestStateManager.clearAttributesForPhase(context,
                                                     context.getCurrentPhaseId());
         while (listenersIterator.hasNext()) {
             PhaseListener listener = listenersIterator.next();
             if (this.getId().equals(listener.getPhaseId()) ||
                 PhaseId.ANY_PHASE.equals(listener.getPhaseId())) {
                 try {
                     listener.beforePhase(event);
                 } catch (Exception e) {
                     queueException(context,
                                    e,
                                    ExceptionQueuedEventContext.IN_BEFORE_PHASE_KEY);
                     // move the iterator pointer back one
                     if (listenersIterator.hasPrevious()) {
                         listenersIterator.previous();
                     }
                     return;
                 }
             }
         }

     }

SystemEvents will be execute in execution time ( not beforePhase, execute, not afterPhase ) of doPhase(). Two methods beforePhase listener.beforePhase(event); and afterPhase listener.afterPhase(event); will execute a PhaseListener.

All kind of SystemEvent:

System Event Description
PreRenderComponentEvent This event is published just before the rendering of the component.
PostAddToViewEvent This event is published just after the component is added to the JSF view.
PreValidateEvent This event is published just before the component is about to be validated.
PostValidateEvent This event is published just after the component is validated.
PreDestroyViewMapEvent This event is published just before the view scope map is about to be destroyed.
PostConstructViewMapEvent This event is published just after the view scope map is created.
PreRenderViewEvent This event is published just before the view (UIViewRoot) is about to be rendered.
PostRestoreStateEvent This event is published just after the component state is restored.

mojarra source code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment