Package org.ajax4jsf.component

Source Code of org.ajax4jsf.component.AjaxViewRoot

/**
* License Agreement.
*
* Rich Faces - Natural Ajax for Java Server Faces (JSF)
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

package org.ajax4jsf.component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;

import javax.el.MethodExpression;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.component.ContextCallback;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.faces.render.Renderer;
import javax.faces.webapp.FacesServlet;

import org.ajax4jsf.Messages;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.context.InvokerCallback;
import org.ajax4jsf.context.ViewIdHolder;
import org.ajax4jsf.event.AjaxListener;
import org.ajax4jsf.event.EventsQueue;
import org.ajax4jsf.renderkit.AjaxContainerRenderer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* Custom ViewRoot for support render parts of tree for Ajax requests. Main
* difference from default ViewRoot - store events queue and unique id counter
* in request-scope variables. In common realisation, store request-scope
* variables in component produce errors in case of concurrent requests to same
* view.
*
* @author asmirnov@exadel.com (latest modification by $Author: alexsmirnov $)
* @version $Revision: 1.1.2.4 $ $Date: 2007/02/28 17:01:01 $
*
*/
public class AjaxViewRoot extends UIViewRoot implements AjaxContainer {

  public static final String ROOT_ID = "_viewRoot";
 
  public static final String JS_NULL = "null";

  private Lifecycle lifecycle;

  private AjaxRegionBrige _brige;

  private static final Log _log = LogFactory.getLog(AjaxViewRoot.class);

  /**
   *
   */
  public AjaxViewRoot() {
    super();
    super.setId(ROOT_ID);
    _brige = new AjaxRegionBrige(this);
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.faces.component.UIComponentBase#getId()
   */
  // public String getId() {
  // return ROOT_ID;
  // }
  public String getRendererType() {
    return (COMPONENT_FAMILY);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#broadcast(javax.faces.event.FacesEvent)
   */
  public void broadcast(FacesEvent event) throws AbortProcessingException {
    super.broadcast(event);
    getBrige().broadcast(event);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#getAjaxListener()
   */
  public MethodExpression getAjaxListener() {
    return getBrige().getAjaxListener();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#isImmediate()
   */
  public boolean isImmediate() {
    return getBrige().isImmediate();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#isSubmitted()
   */
  public boolean isSubmitted() {
    return getBrige().isSubmitted();
  }

  /**
   * Use own {@link PhaseListener}'s list, to use with AJAX processing.
   * phases.
   */
  private List<PhaseListener> phaseListeners = null;

  public void removePhaseListener(PhaseListener toRemove) {
    if (null != phaseListeners) {
      phaseListeners.remove(toRemove);
    }
    super.removePhaseListener(toRemove);
  }

  public void addPhaseListener(PhaseListener newPhaseListener) {
    if (null == phaseListeners) {
      phaseListeners = new ArrayList<PhaseListener>();
    }
    phaseListeners.add(newPhaseListener);
    super.addPhaseListener(newPhaseListener);
  }

  /**
   * Send notification to a view-scope phase listeners.
   *
   * @param context
   * @param phase
   * @param before
   */
  protected void processPhaseListeners(FacesContext context, PhaseId phase,
      boolean before) {
    MethodExpression listenerExpression = before ? getBeforePhaseListener()
        : getAfterPhaseListener();
    PhaseEvent event = null;
    if (null != listenerExpression) {
      event = createPhaseEvent(context, phase);
      listenerExpression.invoke(context.getELContext(),
          new Object[] { event });
    }
    if (null != this.phaseListeners) {
      for (PhaseListener listener : phaseListeners) {
        PhaseId phaseId = listener.getPhaseId();
        if (phaseId == phase || phaseId == PhaseId.ANY_PHASE) {
          if (null == event) {
            event = createPhaseEvent(context, phase);
          }
          if (before) {
            listener.beforePhase(event);
          } else {
            listener.afterPhase(event);
          }
        }
      }
    }
  }

  protected PhaseEvent createPhaseEvent(FacesContext context, PhaseId phaseId)
      throws FacesException {
    if (lifecycle == null) {
      LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
          .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
      String lifecycleId = context.getExternalContext().getInitParameter(
          FacesServlet.LIFECYCLE_ID_ATTR);
      if (lifecycleId == null) {
        lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
      }
      lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
    }
    return (new PhaseEvent(context, phaseId, lifecycle));
  }

  protected void processPhase(FacesContext context, PhaseId phase,
      InvokerCallback callback) {
    // Process phase listeners before phase.
    processPhaseListeners(context, phase, true);
    // Process phase. Run callback method by invokeOnComponent for a
    // submitted region.
    if (!(null == callback || context.getRenderResponse() || context
        .getResponseComplete())) {
      AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
      String submittedRegionClientId = ajaxContext
          .getSubmittedRegionClientId();
      // !submittedRegionClientId.equals(JS_NULL) - fix for myfaces 1.2.4 
      if (ajaxContext.isAjaxRequest() && submittedRegionClientId != null &&
              !submittedRegionClientId.equals(JS_NULL)
              && !submittedRegionClientId.equals(getClientId(context))) {
        invokeOnComponent(context, submittedRegionClientId, new InvokerCallbackWrapper(callback));
      } else {
        // For a root region, call invokeRoot method, then process all
        // facets and children by invoke method.
        try {
          callback.invokeRoot(context);
        } catch (RuntimeException e) {
          context.renderResponse();
          throw e;
        }
        String ajaxSingleClientId = ajaxContext.getAjaxSingleClientId();
        if (null == ajaxSingleClientId) {
          for (Iterator<UIComponent> iter = getFacetsAndChildren(); iter
              .hasNext();) {
            callback.invokeContextCallback(context, iter.next());
          }
        } else {
              InvokerCallback invokerCallback = new InvokerCallbackWrapper(callback);
          invokeOnComponent(context, ajaxSingleClientId, invokerCallback);
          Set<String> areasToProcess = ajaxContext.getAjaxAreasToProcess();
          if(null != areasToProcess){
            for (String areaId : areasToProcess) {
              invokeOnComponent(context, areaId, invokerCallback);
            }
          }
        }
      }
    }
    // Broadcast phase events.
    broadcastEvents(context, phase);
    // Process afterPhase listeners.
    processPhaseListeners(context, phase, false);
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.faces.component.UIComponent#queueEvent(javax.faces.event.FacesEvent)
   */
  public void queueEvent(FacesEvent event) {
    if (event == null)
      throw new NullPointerException(Messages
          .getMessage(Messages.NULL_EVENT_SUBMITTED_ERROR));
    if (event.getPhaseId().compareTo(PhaseId.RENDER_RESPONSE) == 0) {
      // HACK - Special case - Ajax Events to RenderResponse phase
      // queue.
      getAjaxEventsQueue().offer(event);
    } else {
      getEventsQueue(event.getPhaseId()).offer(event);
    }
  }

  /**
   * Broadcast events for specified Phase
   *
   * @param context
   * @param phaseId -
   *            phase, for which events must be processed.
   */
  void broadcastEvents(FacesContext context, PhaseId phaseId) {
    EventsQueue[] events = getEvents();
    EventsQueue anyPhaseEvents = events[PhaseId.ANY_PHASE.getOrdinal()];
    EventsQueue phaseEvents = events[phaseId.getOrdinal()];
    if (phaseEvents.isEmpty() && anyPhaseEvents.isEmpty())
      return;
    // FacesEvent event = null;
    boolean haveAnyPhaseEvents = !anyPhaseEvents.isEmpty();
    boolean havePhaseEvents = !phaseEvents.isEmpty();
    do {
      // ANY_PHASE first
      processEvents(context, anyPhaseEvents, haveAnyPhaseEvents);

      processEvents(context, phaseEvents, havePhaseEvents);
      // Events can queued in other events processing
      haveAnyPhaseEvents = !anyPhaseEvents.isEmpty();
      havePhaseEvents = !phaseEvents.isEmpty();
    } while (haveAnyPhaseEvents || havePhaseEvents);
    if (context.getRenderResponse() || context.getResponseComplete()) {
      clearEvents();
    }

  }

  /**
   * @param context
   *            TODO
   * @param phaseEventsQueue
   * @param havePhaseEvents
   */
  public void processEvents(FacesContext context,
      EventsQueue phaseEventsQueue, boolean havePhaseEvents) {
    FacesEvent event;
    while (havePhaseEvents) {
      try {
        event = (FacesEvent) phaseEventsQueue.remove();
        UIComponent source = event.getComponent();
        try {
          source.broadcast(event);
        } catch (AbortProcessingException e) {
          if (_log.isErrorEnabled()) {
            UIComponent component = event.getComponent();
            String id = null != component ? component
                .getClientId(context) : "";
            _log.error(
                "Error processing faces event for the component "
                    + id, e);
          }
        }
      } catch (NoSuchElementException e) {
        havePhaseEvents = false;
      }
    }
  }

  public void broadcastAjaxEvents(FacesContext context) {
    EventsQueue queue = getAjaxEventsQueue();
    processEvents(context, queue, !queue.isEmpty());
  }

  private EventsQueue[] events;

  private EventsQueue ajaxEvents = new EventsQueue();

  /**
   * Use FIFO buffers for hold Faces Events. Hold this buffers in Request
   * scope parameters for support any concurrent requests for same component
   * tree ( since RI hold component tree in session ).
   *
   * @param phase
   *
   * @return
   */
  protected EventsQueue getEventsQueue(PhaseId phase) {
    return getEvents()[phase.getOrdinal()];
  }

  /**
   * @return
   */
  protected EventsQueue[] getEvents() {
    if (events == null) {
      clearEvents();
    }
    return events;
  }

  /**
   * Special Fifo Buffer for ajax events to Render Responce phase.
   *
   * @return
   */
  protected EventsQueue getAjaxEventsQueue() {

    return ajaxEvents;
  }

  public void clearEvents() {
    int len;
    events = new EventsQueue[len = PhaseId.VALUES.size()];
    for (int i = 0; i < len; i++) {
      events[i] = new EventsQueue();
    }
  }

  private InvokerCallback _decodeInvoker = new InvokerCallback() {

    public void invokeContextCallback(FacesContext context,
        UIComponent component) {
      component.processDecodes(context);
    }

    public void invokeRoot(FacesContext context) {
      decode(context);
    }

  };

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#processDecodes(javax.faces.context.FacesContext)
   */
  public void processDecodes(FacesContext context) {
    if (context == null)
      throw new NullPointerException("context");
    processPhase(context, PhaseId.APPLY_REQUEST_VALUES, _decodeInvoker);
  }

  private InvokerCallback _updatesInvoker = new InvokerCallback() {

    public void invokeContextCallback(FacesContext context,
        UIComponent component) {
      component.processUpdates(context);
    }

    public void invokeRoot(FacesContext context) {
    }

  };

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#processUpdates(javax.faces.context.FacesContext)
   */
  public void processUpdates(FacesContext context) {
    if (context == null)
      throw new NullPointerException("context");
    processPhase(context, PhaseId.UPDATE_MODEL_VALUES, _updatesInvoker);
  }

  private InvokerCallback _validatorsInvoker = new InvokerCallback() {

    public void invokeContextCallback(FacesContext context,
        UIComponent component) {
      component.processValidators(context);
    }

    public void invokeRoot(FacesContext context) {
    }

  };

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#processValidators(javax.faces.context.FacesContext)
   */
  public void processValidators(FacesContext context) {
    if (context == null)
      throw new NullPointerException("context");
    processPhase(context, PhaseId.PROCESS_VALIDATIONS, _validatorsInvoker);
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.faces.component.UIViewRoot#processApplication(javax.faces.context.FacesContext)
   */
  public void processApplication(FacesContext context) {
    if (context == null)
      throw new NullPointerException("context");
    processPhase(context, PhaseId.INVOKE_APPLICATION, null);
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.faces.component.UIViewRoot#encodeBegin(javax.faces.context.FacesContext)
   */
  // public void encodeBegin(FacesContext context) throws IOException {
  // UIComponent submittedComponent = getSubmittedRegion(context);
  // if(null == submittedComponent){
  // super.encodeBegin(context);
  // } else {
  // submittedComponent.encodeBegin(context);
  // }
  // }
  private ContextCallback _ajaxInvoker = new ContextCallback() {

    public void invokeContextCallback(FacesContext context,
        UIComponent component) {
      try {
        if (component instanceof AjaxContainer) {
          AjaxContainer ajax = (AjaxContainer) component;
          ajax.encodeAjax(context);
        } else {
          // Container not found, use Root for encode.
          encodeAjax(context);
        }
      } catch (IOException e) {
        throw new FacesException(e);
      }
    }
  };

  @Override
  public void encodeBegin(FacesContext context) throws IOException {
    processPhaseListeners(context, PhaseId.RENDER_RESPONSE, true);
    // Copy/paste from UIComponentBase, so in no way for java to call super.super method.
        String rendererType = getRendererType();
        if (rendererType != null) {
            Renderer renderer = this.getRenderer(context);
            if (renderer != null) {
                renderer.encodeBegin(context, this);
            } else {
                // TODO: log
            }
        }

  }

  @Override
  public void encodeEnd(FacesContext context) throws IOException {
    // Copy/paste from UIComponentBase, so in no way for java to call super.super method.
        String rendererType = getRendererType();
        if (rendererType != null) {
            Renderer renderer = this.getRenderer(context);
            if (renderer != null) {
                renderer.encodeEnd(context, this);
            } else {
                // TODO: log
            }
        }
    processPhaseListeners(context, PhaseId.RENDER_RESPONSE, false);
    super.encodeEnd(context);
  }
  /*
   * (non-Javadoc)
   *
   * @see javax.faces.component.UIComponentBase#encodeChildren(javax.faces.context.FacesContext)
   */
  public void encodeChildren(FacesContext context) throws IOException {
    AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
    if (ajaxContext.isAjaxRequest()) {
      String submittedRegionClientId = ajaxContext
          .getSubmittedRegionClientId();
      boolean invoked = false;
      if (submittedRegionClientId != null && !submittedRegionClientId.equals(JS_NULL) && !submittedRegionClientId.equals(getClientId(context))) {
        invoked = invokeOnComponent(context, submittedRegionClientId, _ajaxInvoker);
      }
      // if container not found, use Root for encode.
      // https://jira.jboss.org/jira/browse/RF-3975     
      if (!invoked) {
          encodeAjax(context);
      }
    } else {
      super.encodeChildren(context);
    }
  }

  @SuppressWarnings("unchecked")
  public void restoreState(FacesContext context, Object state) {
    Object[] mystate = (Object[]) state;
    super.restoreState(context, mystate[0]);
    getBrige().restoreState(context, mystate[1]);
    Object listeners = restoreAttachedState(context, mystate[2]);
    if (null != listeners) {
      phaseListeners = (List<PhaseListener>) listeners;
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#saveState(javax.faces.context.FacesContext)
   */
  public Object saveState(FacesContext context) {
    Object[] state = new Object[3];
    state[0] = super.saveState(context);
    state[1] = getBrige().saveState(context);
    state[2] = saveAttachedState(context, phaseListeners);
    return state;
  }

  public String getViewId() {
    ViewIdHolder viewIdHolder = AjaxContext.getCurrentInstance()
        .getViewIdHolder();
    String viewId;
    if (null != viewIdHolder) {
      viewId = viewIdHolder.getViewId();
    } else {
      viewId = super.getViewId();
    }
    return viewId;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#setAjaxListener(javax.faces.el.MethodBinding)
   */
  public void setAjaxListener(MethodExpression ajaxListener) {
    getBrige().setAjaxListener(ajaxListener);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#setImmediate(boolean)
   */
  public void setImmediate(boolean immediate) {
    getBrige().setImmediate(immediate);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#setSubmitted(boolean)
   */
  public void setSubmitted(boolean submitted) {
    getBrige().setSubmitted(submitted);
  }

  public void addAjaxListener(AjaxListener listener) {
    addFacesListener(listener);
  }

  public AjaxListener[] getAjaxListeners() {
    return (AjaxListener[]) getFacesListeners(AjaxListener.class);
  }

  public void removeAjaxListener(AjaxListener listener) {
    removeFacesListener(listener);

  }

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#isSelfRendered()
   */
  public boolean isSelfRendered() {
    return false;// _brige.isSelfRendered();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.ajax4jsf.framework.ajax.AjaxViewBrige#setSelfRendered(boolean)
   */
  public void setSelfRendered(boolean selfRendered) {
    // _brige.setSelfRendered(selfRendered);
  }


  /*
   * (non-Javadoc)
   *
   * @see javax.faces.component.UIComponentBase#getRendersChildren()
   */
  public boolean getRendersChildren() {
    FacesContext context = FacesContext.getCurrentInstance();
    // For non Ajax request, view root not render children
    if (!AjaxContext.getCurrentInstance(context).isAjaxRequest()) {
      return false;
    }
    // Ajax Request. Control all output.
    return true;
  }

  public boolean isRenderRegionOnly() {
    // for viewroot it not applicable.
    return false;
  }

  public void setRenderRegionOnly(boolean reRenderPage) {
    // Ignore for a ViewRoot.
  }

  public void encodeAjax(FacesContext context) throws IOException {
    String rendererType = getRendererType();
    if (rendererType != null) {
      ((AjaxContainerRenderer) getRenderer(context)).encodeAjax(context,
          this);
    }

  }

  /**
   * @return the brige
   */
  protected AjaxRegionBrige getBrige() {
    return _brige;
  }

}
TOP

Related Classes of org.ajax4jsf.component.AjaxViewRoot

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.