protected FacesRequestParameterMap getFacesRequestParameterMap(BridgeContext bridgeContext) {

    FacesRequestParameterMap facesRequestParameterMap = null;
    PortletRequest portletRequest = bridgeContext.getPortletRequest();
    PortletResponse portletResponse = bridgeContext.getPortletResponse();
    String namespace = portletResponse.getNamespace();
    BridgeRequestScope bridgeRequestScope = bridgeContext.getBridgeRequestScope();
    String defaultRenderKitId = bridgeContext.getDefaultRenderKitId();
    Map<String, String> facesViewParameterMap = getFacesViewParameterMap(bridgeContext);

    if (portletRequest instanceof ClientDataRequest) {

      ClientDataRequest clientDataRequest = (ClientDataRequest) portletRequest;
      String contentType = clientDataRequest.getContentType();

      // Note: ICEfaces ace:fileEntry relies on its own mechanism for handling file upload.
      if (!ICEFACES_DETECTED
          && (contentType != null)
          && contentType.toLowerCase().startsWith(BridgeConstants.MULTIPART_CONTENT_TYPE_PREFIX)) {

        MultiPartFormData multiPartFormData =
            (MultiPartFormData) portletRequest.getAttribute(MULTIPART_FORM_DATA_FQCN);

        if (multiPartFormData == null) {
          facesRequestParameterMap =
              new FacesRequestParameterMapImpl(
                  namespace, bridgeRequestScope, facesViewParameterMap, defaultRenderKitId);

          MultiPartFormDataProcessor multiPartFormDataProcessor =
              new MultiPartFormDataProcessorImpl();
          Map<String, List<UploadedFile>> uploadedFileMap =
              multiPartFormDataProcessor.process(
                  clientDataRequest, bridgeContext.getPortletConfig(), facesRequestParameterMap);

          multiPartFormData = new MultiPartFormDataImpl(facesRequestParameterMap, uploadedFileMap);

          // Save the multipart/form-data in a request attribute so that it can be referenced
          // later-on in the
          // JSF lifecycle by file upload component renderers.
          portletRequest.setAttribute(MULTIPART_FORM_DATA_FQCN, multiPartFormData);
        } else {
          facesRequestParameterMap = multiPartFormData.getFacesRequestParameterMap();
        }
      }
    }

    if (facesRequestParameterMap == null) {
      Map<String, String[]> parameterMap = portletRequest.getParameterMap();
      facesRequestParameterMap =
          new FacesRequestParameterMapImpl(
              parameterMap,
              namespace,
              bridgeRequestScope,
              facesViewParameterMap,
              defaultRenderKitId);
    }

    return facesRequestParameterMap;
  }
  /**
   * This method is called after an attribute is added to the ServletRequest. Note that this should
   * only get called for remote WSRP portlets. For more info, see:
   * http://issues.liferay.com/browse/FACES-146
   */
  public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {

    // NOTE: We only care about phases prior to the RENDER_PHASE because we're concerned here about
    // managed beans
    // that get added to the request scope when the BridgeRequestScope begins. We're trying to
    // provide those managed
    // beans with an opportunity to prepare for an unexpected invocation of their methods annotated
    // with
    // @PreDestroy.
    ServletRequest servletRequest = servletRequestAttributeEvent.getServletRequest();
    PortletPhase phase = (PortletPhase) servletRequest.getAttribute(Bridge.PORTLET_LIFECYCLE_PHASE);

    // If this is taking place within a PortletRequest handled by the bridge in any phase prior to
    // the
    // RENDER_PHASE, then
    if ((phase != null) && (phase != PortletPhase.RENDER_PHASE)) {

      // If the attribute being added is not excluded, then invoke all methods on the attribute
      // value (class
      // instance) that are annotated with the BridgeRequestScopeAttributeAdded annotation.
      String attributeName = servletRequestAttributeEvent.getName();
      BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
      BridgeConfig bridgeConfig = bridgeContext.getBridgeConfig();
      Set<String> excludedRequestScopeAttributes = bridgeConfig.getExcludedRequestAttributes();

      if (!excludedRequestScopeAttributes.contains(attributeName)) {

        Object attributeValue = servletRequestAttributeEvent.getValue();
        logger.trace("Attribute added name=[{0}] value=[{1}]", attributeName, attributeValue);

        if (attributeValue != null) {
          Method[] methods = attributeValue.getClass().getMethods();

          if (methods != null) {

            for (Method method : methods) {

              if (method != null) {

                if (method.isAnnotationPresent(BridgeRequestScopeAttributeAdded.class)) {

                  try {
                    method.invoke(attributeValue, new Object[] {});
                  } catch (Exception e) {
                    logger.error(e);
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  public RequestParameterMapFactory(BridgeContext bridgeContext) {

    PortletRequest portletRequest = bridgeContext.getPortletRequest();

    if (portletRequest instanceof ClientDataRequest) {
      ClientDataRequest clientDataRequest = (ClientDataRequest) portletRequest;
      String contentType = clientDataRequest.getContentType();

      // Note that ICEfaces ace:fileEntry cannot rely on RequestParameterValuesMapImpl because it
      // relies on its
      // own mechanism for handling file upload.
      Product iceFaces = ProductMap.getInstance().get(ProductConstants.ICEFACES);

      if ((contentType != null)
          && contentType.toLowerCase().startsWith(BridgeConstants.MULTIPART_CONTENT_TYPE_PREFIX)
          && !iceFaces.isDetected()) {
        RequestParameterMapMultiPartImpl requestParameterMapMultiPartImpl =
            new RequestParameterMapMultiPartImpl(bridgeContext, clientDataRequest);
        requestParameterMap = requestParameterMapMultiPartImpl;
        requestParameterValuesMap =
            new RequestParameterValuesMapMultiPartImpl(requestParameterMapMultiPartImpl);
      } else {
        requestParameterMap = new RequestParameterMapImpl(bridgeContext);
        requestParameterValuesMap = new RequestParameterValuesMapImpl(bridgeContext);
      }
    } else {
      requestParameterMap = new RequestParameterMapImpl(bridgeContext);
      requestParameterValuesMap = new RequestParameterValuesMapImpl(bridgeContext);
    }
  }
  @Override
  public Map<String, Object> getRequestCookieMap(BridgeContext bridgeContext) {
    PortletRequest portletRequest = bridgeContext.getPortletRequest();
    Cookie[] cookies = portletRequest.getCookies();

    return new RequestCookieMap(cookies);
  }
  @Override
  public Map<String, String> getFacesViewParameterMap(BridgeContext bridgeContext) {

    String facesViewQueryString = bridgeContext.getFacesViewQueryString();

    return new FacesViewParameterMap(facesViewQueryString);
  }
  /**
   * This method is called prior to the {@link PhaseId#RENDER_RESPONSE} phase of the JSF lifecycle.
   */
  @Override
  public void beforePhase(PhaseEvent phaseEvent) {

    // Determine if there are any resources in the LIFERAY_SHARED_PAGE_TOP request attribute, so
    // that execution of
    // the {@link #afterPhase(PhaseEvent)} can be optimized.
    liferaySharedPageTopLength = 0;

    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
    PortletRequest portletRequest = bridgeContext.getPortletRequest();
    StringBundler pageTop = getPageTop(portletRequest);

    if (pageTop != null) {
      liferaySharedPageTopLength = pageTop.length();
    }
  }
  /**
   * Note: The reason why this method appears here in {@link ExternalContextCompat_1_2_Impl} is
   * because the method was first introduced with JSF 1.0 and and also because it needs to be
   * overridden by {@link ExternalContextCompat_2_2_Impl} since it has special requirements for JSF
   * 2.2.
   *
   * @see {@link ExternalContext#encodeActionURL(String, Map)}
   * @since JSF 1.0
   */
  @Override
  public String encodeActionURL(String url) {

    if (isEncodingFormWithPrimeFacesAjaxFileUpload()) {
      return encodePartialActionURL(url);
    } else {
      return bridgeContext.encodeActionURL(url).toString();
    }
  }
  public ExternalContextCompat_1_2_Impl(
      PortletContext portletContext,
      PortletRequest portletRequest,
      PortletResponse portletResponse) {

    this.portletContext = portletContext;
    this.portletRequest = portletRequest;
    this.portletResponse = portletResponse;

    // Get the BridgeContext.
    this.bridgeContext = BridgeContext.getCurrentInstance();

    this.incongruityContext = bridgeContext.getIncongruityContext();

    // Determine whether or not lifecycle incongruities should be managed.
    PortletConfig portletConfig = bridgeContext.getPortletConfig();
    this.manageIncongruities =
        PortletConfigParam.ManageIncongruities.getBooleanValue(portletConfig);
  }
  public ApplicationScopeMap(BridgeContext bridgeContext) {

    BeanManagerFactory beanManagerFactory =
        (BeanManagerFactory) BridgeFactoryFinder.getFactory(BeanManagerFactory.class);
    this.portletContext = bridgeContext.getPortletContext();

    String appConfigAttrName = ApplicationConfig.class.getName();
    ApplicationConfig applicationConfig =
        (ApplicationConfig) this.portletContext.getAttribute(appConfigAttrName);
    this.beanManager = beanManagerFactory.getBeanManager(applicationConfig.getFacesConfig());

    // Determines whether or not methods annotated with the @PreDestroy annotation are preferably
    // invoked
    // over the @BridgePreDestroy annotation.
    PortletConfig portletConfig = bridgeContext.getPortletConfig();
    this.preferPreDestroy = PortletConfigParam.PreferPreDestroy.getBooleanValue(portletConfig);

    PreDestroyInvokerFactory preDestroyInvokerFactory =
        (PreDestroyInvokerFactory) BridgeFactoryFinder.getFactory(PreDestroyInvokerFactory.class);
    this.preDestroyInvoker = preDestroyInvokerFactory.getPreDestroyInvoker(this);
  }
  @Override
  public Map<String, List<UploadedFile>> getUploadedFileMap(BridgeContext bridgeContext) {

    PortletRequest portletRequest = bridgeContext.getPortletRequest();
    MultiPartFormData multiPartFormData =
        (MultiPartFormData) portletRequest.getAttribute(MULTIPART_FORM_DATA_FQCN);
    Map<String, List<UploadedFile>> uploadedFileMap = null;

    if (multiPartFormData != null) {
      uploadedFileMap = multiPartFormData.getUploadedFileMap();
    }

    return uploadedFileMap;
  }
  /** This method is called after the {@link PhaseId#RENDER_RESPONSE} phase of the JSF lifecycle. */
  @Override
  public void afterPhase(PhaseEvent phaseEvent) {

    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();

    // Remove duplicate resources from the LIFERAY_SHARED_PAGE_TOP request attribute. For more
    // information, see:
    // http://issues.liferay.com/browse/FACES-1216
    if (liferaySharedPageTopLength > 0) {

      PortletRequest portletRequest = bridgeContext.getPortletRequest();

      StringBundler pageTop = getPageTop(portletRequest);

      if (pageTop != null) {

        LiferaySharedPageTop liferaySharedPageTop = new LiferaySharedPageTop(pageTop);
        liferaySharedPageTop.removeDuplicates();
        pageTop = liferaySharedPageTop.toStringBundler();

        setPageTop(portletRequest, pageTop);
      }
    }
  }
  /**
   * This method overrides the {@link #decode(FacesContext, UIComponent)} method so that it can
   * avoid a Servlet-API dependency in the RichFaces FileUploadRenderer. Note that rich:fileUpload
   * will do an Ajax postback and invoke the JSF lifecycle for each individual file.
   */
  @Override
  public void decode(FacesContext facesContext, UIComponent uiComponent) {

    try {

      // Get the UploadedFile from the request attribute map.
      ContextMapFactory contextMapFactory =
          (ContextMapFactory) FactoryExtensionFinder.getFactory(ContextMapFactory.class);
      BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
      Map<String, Collection<UploadedFile>> uploadedFileMap =
          contextMapFactory.getUploadedFileMap(bridgeContext);

      if (uploadedFileMap != null) {

        // Use reflection to create a dynamic proxy class that implements the RichFaces UploadedFile
        // interface.
        Class<?> uploadedFileInterface = Class.forName(RICHFACES_UPLOADED_FILE_FQCN);
        Class<?> fileUploadEventClass = Class.forName(RICHFACES_FILE_UPLOAD_EVENT_FQCN);
        ClassLoader classLoader = uploadedFileInterface.getClassLoader();

        String clientId = uiComponent.getClientId(facesContext);
        Collection<UploadedFile> uploadedFiles = uploadedFileMap.get(clientId);

        if (uploadedFiles != null) {

          for (UploadedFile uploadedFile : uploadedFiles) {
            RichFacesUploadedFileHandler richFacesUploadedFileHandler =
                new RichFacesUploadedFileHandler(uploadedFile);
            Object richFacesUploadedFile =
                Proxy.newProxyInstance(
                    classLoader, new Class[] {uploadedFileInterface}, richFacesUploadedFileHandler);
            FacesEvent fileUploadEvent =
                (FacesEvent)
                    fileUploadEventClass
                        .getConstructor(UIComponent.class, uploadedFileInterface)
                        .newInstance(uiComponent, richFacesUploadedFile);

            // Queue the RichFaces FileUploadEvent instance so that it can be handled with an
            // ActionListener.
            uiComponent.queueEvent(fileUploadEvent);
          }
        }
      }
    } catch (Exception e) {
      logger.error(e);
    }
  }
  /**
   * Saves the state of the FacesContext as required by section 5.1.2 of the JSR 329 spec. This
   * method is designed to be called during the ACTION_PHASE of the portlet lifecycle.
   *
   * @param facesContext The current faces context.
   */
  public void saveState(FacesContext facesContext) {

    logger.debug("saveState(facesContext)");

    // Get the ExternalContext and PortletResponse.
    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();
    PortletResponse portletResponse =
        (PortletResponse) facesContext.getExternalContext().getResponse();

    if ((beganInPhase == Bridge.PortletPhase.ACTION_PHASE)
        || (beganInPhase == Bridge.PortletPhase.EVENT_PHASE)
        || (beganInPhase == Bridge.PortletPhase.RESOURCE_PHASE)) {

      // Save the view root.
      setAttribute(BRIDGE_REQ_SCOPE_ATTR_FACES_VIEW_ROOT, facesContext.getViewRoot());

      // If the PortletMode hasn't changed, then preserve the "javax.faces.ViewState" request
      // parameter value.
      if (!isPortletModeChanged()) {

        if (portletResponse instanceof ActionResponse) {
          String viewState =
              facesContext
                  .getExternalContext()
                  .getRequestParameterMap()
                  .get(ResponseStateManager.VIEW_STATE_PARAM);

          if (viewState != null) {

            // NOTE: Although it is possible to save this as a render parameter, can't use that
            // approach
            // because portlet containers like Pluto will add the "javax.faces.ViewState" parameter
            // to any
            // ResourceURLs that are created during the RENDER_PHASE of the portlet lifecycle.
            setAttribute(ResponseStateManager.VIEW_STATE_PARAM, viewState);
          }
        }
      }

      // If specified in the WEB-INF/portlet.xml descriptor, then preserve the action parameters.
      if (bridgeContext.isPreserveActionParams()) {
        Map<String, String> actionRequestParameterMap =
            new HashMap<String, String>(externalContext.getRequestParameterMap());
        actionRequestParameterMap.remove(ResponseStateManager.VIEW_STATE_PARAM);
        actionRequestParameterMap.remove(JAVAX_FACES_ENCODED_URL_PARAM);
        setAttribute(BRIDGE_REQ_SCOPE_ATTR_ACTION_PARAMS, actionRequestParameterMap);
      }

      // Save the list of faces messages.
      List<FacesMessageWrapper> facesMessageWrappers = new ArrayList<FacesMessageWrapper>();
      Iterator<String> clientIds = facesContext.getClientIdsWithMessages();

      while (clientIds.hasNext()) {
        String clientId = clientIds.next();
        Iterator<FacesMessage> facesMessages = facesContext.getMessages(clientId);

        while (facesMessages.hasNext()) {
          FacesMessage facesMessage = facesMessages.next();
          FacesMessageWrapper facesMessageWrapper = new FacesMessageWrapper(clientId, facesMessage);
          facesMessageWrappers.add(facesMessageWrapper);
        }
      }

      if (facesMessageWrappers.size() > 0) {
        setAttribute(BRIDGE_REQ_SCOPE_ATTR_FACES_MESSAGES, facesMessageWrappers);
      } else {
        logger.trace("Not saving any faces messages");
      }

      // NOTE: PROPOSED-FOR-BRIDGE3-API: https://issues.apache.org/jira/browse/PORTLETBRIDGE-203
      // Build up a list
      // of attributes found in the FacesContext attribute map and save them. It has to be copied in
      // this manner
      // because the Faces implementation likely calls the clear() method during the call to its
      // FacesContextImpl.release() method.
      saveJSF2FacesContextAttributes(facesContext);
    }

    if ((beganInPhase == Bridge.PortletPhase.ACTION_PHASE)
        || (beganInPhase == Bridge.PortletPhase.EVENT_PHASE)
        || (beganInPhase == Bridge.PortletPhase.RESOURCE_PHASE)) {

      boolean saveNonExcludedAttributes = true;

      // If a redirect occurred, then indicate that the non-excluded request attributes are not to
      // be preserved.
      if (isRedirectOccurred()) {

        // TCK TestPage062: eventScopeNotRestoredRedirectTest
        logger.trace("Due to redirect, not saving any non-excluded request attributes");
        saveNonExcludedAttributes = false;
      }

      // Otherwise, if the portlet mode has changed, then indicate that the non-exluded request
      // attributes are
      // not to be preserved.
      else if (isPortletModeChanged()) {
        logger.trace("Due to PortletMode change, not saving any non-excluded request attributes");
        saveNonExcludedAttributes = false;
      }

      // If appropriate, save the non-excluded request attributes. This would include, for example,
      // managed-bean
      // instances that may have been created during the ACTION_PHASE that need to survive to the
      // RENDER_PHASE.
      Map<String, Object> currentRequestAttributes = externalContext.getRequestMap();

      if (currentRequestAttributes != null) {
        List<RequestAttribute> savedRequestAttributes = new ArrayList<RequestAttribute>();
        List<String> nonExcludedAttributeNames = new ArrayList<String>();
        Iterator<Map.Entry<String, Object>> itr = currentRequestAttributes.entrySet().iterator();

        if (itr != null) {

          while (itr.hasNext()) {
            Map.Entry<String, Object> mapEntry = itr.next();
            String attributeName = mapEntry.getKey();
            Object attributeValue = mapEntry.getValue();

            if (isExcludedRequestAttributeByConfig(attributeName, attributeValue)
                || isExcludedRequestAttributeByAnnotation(attributeValue)
                || isExcludedRequestAttributeByNamespace(attributeName)
                || isExcludedRequestAttributeByInstance(attributeName, attributeValue)
                || isExcludedRequestAttributeByPreExisting(attributeName)) {

              logger.trace("NOT saving EXCLUDED attribute name=[{0}]", attributeName);
            } else {

              if (saveNonExcludedAttributes) {
                logger.trace(
                    "SAVING non-excluded request attribute name=[{0}] value=[{1}]",
                    attributeName, attributeValue);
                savedRequestAttributes.add(new RequestAttribute(attributeName, attributeValue));
              }

              nonExcludedAttributeNames.add(attributeName);
            }
          }

          if (savedRequestAttributes.size() > 0) {
            setAttribute(BRIDGE_REQ_SCOPE_ATTR_REQUEST_ATTRIBUTES, savedRequestAttributes);
          } else {
            logger.trace("Not saving any non-excluded request attributes");
          }

          setAttribute(BRIDGE_REQ_SCOPE_NON_EXCLUDED_ATTR_NAMES, nonExcludedAttributeNames);
        }
      } else {
        logger.trace(
            "Not saving any non-excluded request attributes because there are no request attributes!");
      }
    }

    // If running in the ACTION_PHASE or EVENT_PHASE, then the Flash scope must be saved as well so
    // that it can be
    // restored.
    Bridge.PortletPhase portletRequestPhase = bridgeContext.getPortletRequestPhase();

    if ((portletRequestPhase == Bridge.PortletPhase.ACTION_PHASE)
        || (portletRequestPhase == Bridge.PortletPhase.EVENT_PHASE)) {

      // PROPOSED-FOR-JSR344-API: http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1070
      // PROPOSED-FOR-BRIDGE3-API: https://issues.apache.org/jira/browse/PORTLETBRIDGE-201
      saveFlashState(facesContext);
    }

    // If running in the ACTION_PHASE or EVENT_PHASE, then the incongruity context must be saved as
    // well so that it
    // can be restored.
    if ((portletRequestPhase == Bridge.PortletPhase.ACTION_PHASE)
        || (portletRequestPhase == Bridge.PortletPhase.EVENT_PHASE)) {

      IncongruityContext incongruityContext = bridgeContext.getIncongruityContext();
      Map<String, Object> incongruityAttributeMap = incongruityContext.getAttributes();
      int mapSize = incongruityAttributeMap.size();
      List<IncongruityAttribute> savedIncongruityAttributes =
          new ArrayList<IncongruityAttribute>(mapSize);
      Iterator<Map.Entry<String, Object>> itr = incongruityAttributeMap.entrySet().iterator();

      while (itr.hasNext()) {
        Map.Entry<String, Object> mapEntry = itr.next();
        String name = mapEntry.getKey();
        Object value = mapEntry.getValue();
        logger.trace("Saving IncongruityContext attribute name=[{0}] value=[{1}]", name, value);
        savedIncongruityAttributes.add(new IncongruityAttribute(name, value));
      }

      setAttribute(
          BRIDGE_REQ_SCOPE_ATTR_INCONGRUITY_CONTEXT_ATTRIBUTES, savedIncongruityAttributes);
    }
  }
 public BridgeResourceURLImpl(String url, String currentFacesViewId, BridgeContext bridgeContext) {
   super(url, currentFacesViewId, bridgeContext);
   this.portletContainer = bridgeContext.getPortletContainer();
 }
  @SuppressWarnings("unchecked")
  public void restoreState(FacesContext facesContext) {

    logger.debug("restoreState(facesContext)");

    boolean restoreNonExcludedRequestAttributes =
        ((beganInPhase == Bridge.PortletPhase.ACTION_PHASE)
            || (beganInPhase == Bridge.PortletPhase.EVENT_PHASE)
            || (beganInPhase == Bridge.PortletPhase.RESOURCE_PHASE));

    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();

    PortletPhase portletRequestPhase = bridgeContext.getPortletRequestPhase();

    if (portletRequestPhase == Bridge.PortletPhase.RENDER_PHASE) {

      if (!portletMode.equals(bridgeContext.getPortletRequest().getPortletMode())) {
        setPortletModeChanged(true);
        restoreNonExcludedRequestAttributes = false;
      }
    }

    if ((beganInPhase == Bridge.PortletPhase.ACTION_PHASE)
        || (beganInPhase == Bridge.PortletPhase.EVENT_PHASE)
        || (beganInPhase == Bridge.PortletPhase.RESOURCE_PHASE)) {

      // Restore the view root that may have been saved during the ACTION_PHASE of the portlet
      // lifecycle.
      UIViewRoot uiViewRoot = (UIViewRoot) getAttribute(BRIDGE_REQ_SCOPE_ATTR_FACES_VIEW_ROOT);

      if (uiViewRoot != null) {
        facesContext.setViewRoot(uiViewRoot);
        logger.debug("Restored viewId=[{0}] uiViewRoot=[{1}]", uiViewRoot.getViewId(), uiViewRoot);
      } else {
        logger.debug("Did not restore uiViewRoot");
      }

      // Restore the faces messages that may have been saved during the ACTION_PHASE of the portlet
      // lifecycle.
      List<FacesMessageWrapper> facesMessages =
          (List<FacesMessageWrapper>) getAttribute(BRIDGE_REQ_SCOPE_ATTR_FACES_MESSAGES);

      boolean restoredFacesMessages = false;

      if (facesMessages != null) {

        for (FacesMessageWrapper facesMessageWrapper : facesMessages) {
          String clientId = facesMessageWrapper.getClientId();
          FacesMessage facesMessage = facesMessageWrapper.getFacesMessage();
          facesContext.addMessage(clientId, facesMessage);
          logger.trace("Restored facesMessage=[{0}]", facesMessage.getSummary());
          restoredFacesMessages = true;
        }
      }

      if (restoredFacesMessages) {
        logger.debug("Restored facesMessages");
      } else {
        logger.debug("Did not restore any facesMessages");
      }

      // NOTE: PROPOSE-FOR-BRIDGE3-API: https://issues.apache.org/jira/browse/PORTLETBRIDGE-203
      // Restore the
      // FacesContext attributes that may have been saved during the ACTION_PHASE of the portlet
      // lifecycle.
      restoreJSF2FacesContextAttributes(facesContext);
    }

    if (restoreNonExcludedRequestAttributes) {

      // Restore the non-excluded request attributes.
      List<RequestAttribute> savedRequestAttributes =
          (List<RequestAttribute>) getAttribute(BRIDGE_REQ_SCOPE_ATTR_REQUEST_ATTRIBUTES);

      boolean restoredNonExcludedRequestAttributes = false;

      if (savedRequestAttributes != null) {
        Map<String, Object> currentRequestAttributes =
            facesContext.getExternalContext().getRequestMap();

        // If a redirect did not occur, then restore the non-excluded request attributes.
        if (!isRedirectOccurred()) {

          for (RequestAttribute requestAttribute : savedRequestAttributes) {
            String name = requestAttribute.getName();
            Object value = requestAttribute.getValue();
            logger.trace(
                "Restoring non-excluded request attribute name=[{0}] value=[{1}]", name, value);
            currentRequestAttributes.put(name, value);
            restoredNonExcludedRequestAttributes = true;
          }
        }
      }

      if (restoredNonExcludedRequestAttributes) {
        logger.debug("Restored non-excluded request attributes");
      } else {
        logger.debug("Did not restore any non-excluded request attributes");
      }
    }

    // If running in the RENDER_PHASE, then the Flash scope must be restored.
    if (portletRequestPhase == Bridge.PortletPhase.RENDER_PHASE) {

      // NOTE: PROPOSED-FOR-BRIDGE3-API: https://issues.apache.org/jira/browse/PORTLETBRIDGE-201
      // Restore the flash scope.
      restoreFlashState(facesContext);
    }

    // If running in the RENDER_PHASE, then the incongruity context must be restored.
    if (((beganInPhase == Bridge.PortletPhase.ACTION_PHASE)
            || (beganInPhase == Bridge.PortletPhase.EVENT_PHASE))
        && (portletRequestPhase == Bridge.PortletPhase.RENDER_PHASE)) {

      List<IncongruityAttribute> savedIncongruityAttributes =
          (List<IncongruityAttribute>)
              getAttribute(BRIDGE_REQ_SCOPE_ATTR_INCONGRUITY_CONTEXT_ATTRIBUTES);

      if (savedIncongruityAttributes != null) {

        IncongruityContext incongruityContext = bridgeContext.getIncongruityContext();
        Map<String, Object> incongruityContextAttributes = incongruityContext.getAttributes();

        for (IncongruityAttribute incongruityAttribute : savedIncongruityAttributes) {
          String key = incongruityAttribute.getName();
          Object value = incongruityAttribute.getValue();
          incongruityContextAttributes.put(key, value);
        }
      }
    }
  }
  /**
   * Saves the state of the FacesContext as required by section 5.1.2 of the JSR 329 spec. This
   * method is designed to be called during the ACTION_PHASE of the portlet lifecycle.
   *
   * @param facesContext The current faces context.
   */
  public void preserveScopedData(FacesContext facesContext) {

    logger.debug("preserveScopedData(facesContext)");

    // Get the ExternalContext.
    ExternalContext externalContext = facesContext.getExternalContext();

    // Save the view root.
    setAttribute(BRIDGE_REQ_SCOPE_ATTR_FACES_VIEW_ROOT, facesContext.getViewRoot());

    // If the PortletMode hasn't changed, then preserve the "javax.faces.ViewState" request
    // parameter value.
    if (!portletModeChanged) {
      PortletResponse portletResponse =
          (PortletResponse) facesContext.getExternalContext().getResponse();

      if (portletResponse instanceof ActionResponse) {
        String viewState =
            facesContext
                .getExternalContext()
                .getRequestParameterMap()
                .get(ResponseStateManager.VIEW_STATE_PARAM);

        if (viewState != null) {

          // NOTE: Although it is possible to save this as a render parameter, can't use that
          // approach because
          // portlet containers like Pluto will add the "javax.faces.ViewState" parameter to any
          // ResourceURLs
          // that are created during the RENDER_PHASE of the portlet lifecycle.
          setAttribute(ResponseStateManager.VIEW_STATE_PARAM, viewState);
        }
      }
    }

    // If specified in the WEB-INF/portlet.xml descriptor, then preserve the action parameters.
    BridgeContext bridgeContext =
        (BridgeContext) facesContext.getAttributes().get(BridgeExt.BRIDGE_CONTEXT_ATTRIBUTE);

    if (bridgeContext.isPreserveActionParams()) {
      Map<String, String> actionRequestParameterMap =
          new HashMap<String, String>(externalContext.getRequestParameterMap());
      actionRequestParameterMap.remove(ResponseStateManager.VIEW_STATE_PARAM);
      actionRequestParameterMap.remove(JAVAX_FACES_ENCODED_URL_PARAM);
      setAttribute(BRIDGE_REQ_SCOPE_ATTR_ACTION_PARAMS, actionRequestParameterMap);
    }

    // Save the list of faces messages.
    List<FacesMessageWrapper> facesMessageWrappers = new ArrayList<FacesMessageWrapper>();
    Iterator<String> clientIds = facesContext.getClientIdsWithMessages();

    while (clientIds.hasNext()) {
      String clientId = clientIds.next();
      Iterator<FacesMessage> facesMessages = facesContext.getMessages(clientId);

      while (facesMessages.hasNext()) {
        FacesMessage facesMessage = facesMessages.next();
        FacesMessageWrapper facesMessageWrapper = new FacesMessageWrapper(clientId, facesMessage);
        facesMessageWrappers.add(facesMessageWrapper);
      }
    }

    if (facesMessageWrappers.size() > 0) {
      setAttribute(BRIDGE_REQ_SCOPE_ATTR_FACES_MESSAGES, facesMessageWrappers);
    } else {
      logger.trace("Not saving any faces messages");
    }

    // Save the non-excluded request attributes. This would include, for example, managed-bean
    // instances that may
    // have been created during the ACTION_PHASE that need to survive to the RENDER_PHASE.
    if ((!redirect) && (!portletModeChanged)) {
      Map<String, Object> currentRequestAttributes = externalContext.getRequestMap();

      if (currentRequestAttributes != null) {
        List<RequestAttribute> savedRequestAttributes = new ArrayList<RequestAttribute>();
        Iterator<Map.Entry<String, Object>> itr = currentRequestAttributes.entrySet().iterator();

        if (itr != null) {

          while (itr.hasNext()) {
            Map.Entry<String, Object> mapEntry = itr.next();
            String name = mapEntry.getKey();
            Object value = mapEntry.getValue();

            if (isExcludedRequestAttribute(name, value)) {
              logger.trace("Not saving EXCLUDED attribute name=[{0}]", name);
            } else if ((value != null)
                && (value.getClass().getAnnotation(ExcludeFromManagedRequestScope.class) != null)) {
              logger.trace(
                  "Not saving EXCLUDED attribute name=[{0}] due to ExcludeFromManagedRequestScope annotation",
                  name);
            } else {
              logger.trace(
                  "Saving non-excluded request attribute name=[{0}] value=[{1}]", name, value);
              savedRequestAttributes.add(new RequestAttribute(name, value));
            }
          }

          if (savedRequestAttributes.size() > 0) {
            setAttribute(BRIDGE_REQ_SCOPE_ATTR_REQUEST_ATTRIBUTES, savedRequestAttributes);
          } else {
            logger.trace("Not saving any non-excluded request attributes");
          }
        }
      } else {
        logger.trace(
            "Not saving any non-excluded request attributes because there are no request attributes!");
      }
    } else {
      logger.trace("Not saving any non-excluded request attributes due to redirect");
    }

    // NOTE: PROPOSED-FOR-BRIDGE3-API: https://issues.apache.org/jira/browse/PORTLETBRIDGE-203 Build
    // up a list of
    // attributes found in the FacesContext attribute map and save them. It has to be copied in this
    // manner because
    // the Faces implementation likely calls the clear() method during the call to its
    // FacesContextImpl.release()
    // method.
    Map<Object, Object> currentFacesContextAttributes = facesContext.getAttributes();
    int mapSize = currentFacesContextAttributes.size();
    List<FacesContextAttribute> savedFacesContextAttributes =
        new ArrayList<FacesContextAttribute>(mapSize);
    Iterator<Map.Entry<Object, Object>> itr = currentFacesContextAttributes.entrySet().iterator();

    while (itr.hasNext()) {
      Map.Entry<Object, Object> mapEntry = itr.next();
      Object name = mapEntry.getKey();
      Object value = mapEntry.getValue();
      logger.trace("Saving FacesContext attribute name=[{0}] value=[{1}]", name, value);
      savedFacesContextAttributes.add(new FacesContextAttribute(name, value));
    }

    setAttribute(BRIDGE_REQ_SCOPE_ATTR_FACES_CONTEXT_ATTRIBUTES, savedFacesContextAttributes);
  }
  @SuppressWarnings("unchecked")
  public RequestParameterMapMultiPartImpl(
      BridgeContext bridgeContext, ClientDataRequest clientDataRequest) {

    try {

      PortletSession portletSession = clientDataRequest.getPortletSession();
      PortletContext portletContext = portletSession.getPortletContext();

      // Determine the uploaded files directory path according to the JSF 2.2 proposal:
      // https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=690
      String uploadedFilesDir = portletContext.getInitParameter(CONTEXT_PARAM_UPLOADED_FILES_DIR);

      if (uploadedFilesDir == null) {
        uploadedFilesDir = System.getProperty(JAVA_IO_TMPDIR);

        if (logger.isDebugEnabled()) {
          logger.debug(
              "The web.xml context-param name=[{0}] not found, using default system property=[{1}] value=[{2}]",
              new Object[] {CONTEXT_PARAM_UPLOADED_FILES_DIR, JAVA_IO_TMPDIR, uploadedFilesDir});
        }
      } else {

        if (logger.isDebugEnabled()) {
          logger.debug(
              "Using web.xml context-param name=[{0}] value=[{1}]",
              new Object[] {CONTEXT_PARAM_UPLOADED_FILES_DIR, uploadedFilesDir});
        }
      }

      // Using the portlet sessionId, determine a unique folder path and create the path if it does
      // not exist.
      String sessionId = portletSession.getId();
      File uploadedFilesPath = new File(uploadedFilesDir, sessionId);

      if (!uploadedFilesPath.exists()) {

        try {
          uploadedFilesPath.mkdirs();
        } catch (SecurityException e) {
          uploadedFilesDir = System.getProperty(JAVA_IO_TMPDIR);
          logger.error(
              "Security exception message=[{0}] when trying to create unique path=[{1}] so using default system property=[{2}] value=[{3}]",
              new Object[] {
                e.getMessage(), uploadedFilesPath.toString(), JAVA_IO_TMPDIR, uploadedFilesDir
              });
          uploadedFilesPath = new File(uploadedFilesDir, sessionId);
          uploadedFilesPath.mkdirs();
        }
      }

      // Initialize commons-fileupload with the file upload path.
      DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
      diskFileItemFactory.setRepository(uploadedFilesPath);

      // Initialize commons-fileupload so that uploaded temporary files are not automatically
      // deleted.
      diskFileItemFactory.setFileCleaningTracker(null);

      // Initialize the commons-fileupload size threshold to zero, so that all files will be dumped
      // to disk
      // instead of staying in memory.
      diskFileItemFactory.setSizeThreshold(0);

      // Determine the max file upload size threshold in bytes.
      String uploadedFilesMaxSize =
          portletContext.getInitParameter(CONTEXT_PARAM_UPLOADED_FILE_MAX_SIZE);
      int fileMaxSize = DEFAULT_FILE_MAX_SIZE;

      if (uploadedFilesMaxSize == null) {

        if (logger.isDebugEnabled()) {
          logger.debug(
              "The web.xml context-param name=[{0}] not found, using default=[{1}] bytes",
              new Object[] {CONTEXT_PARAM_UPLOADED_FILE_MAX_SIZE, DEFAULT_FILE_MAX_SIZE});
        }
      } else {

        try {
          fileMaxSize = Integer.parseInt(uploadedFilesMaxSize);

          if (logger.isDebugEnabled()) {
            logger.debug(
                "Using web.xml context-param name=[{0}] value=[{1}] bytes",
                new Object[] {CONTEXT_PARAM_UPLOADED_FILE_MAX_SIZE, fileMaxSize});
          }
        } catch (NumberFormatException e) {
          logger.error(
              "Invalid value=[{0}] for web.xml context-param name=[{1}] using default=[{2}] bytes.",
              new Object[] {
                uploadedFilesMaxSize, CONTEXT_PARAM_UPLOADED_FILE_MAX_SIZE, DEFAULT_FILE_MAX_SIZE
              });
        }
      }

      // Parse the request parameters and save all uploaded files in a map.
      PortletFileUpload portletFileUpload = new PortletFileUpload(diskFileItemFactory);
      portletFileUpload.setFileSizeMax(fileMaxSize);
      requestParameterMap = new HashMap<String, String>();
      requestParameterFileMap = new HashMap<String, List<UploadedFile>>();

      // Get the namespace that might be found in request parameter names.
      String namespace = bridgeContext.getPortletContainer().getResponseNamespace();

      // FACES-271: Include name+value pairs found in the ActionRequest.
      PortletContainer portletContainer = bridgeContext.getPortletContainer();
      Set<Map.Entry<String, String[]>> actionRequestParameterSet =
          clientDataRequest.getParameterMap().entrySet();

      for (Map.Entry<String, String[]> mapEntry : actionRequestParameterSet) {

        String parameterName = mapEntry.getKey();
        int pos = parameterName.indexOf(namespace);

        if (pos >= 0) {
          parameterName = parameterName.substring(pos + namespace.length());
        }

        String[] parameterValues = mapEntry.getValue();

        if (parameterValues.length > 0) {
          String fixedRequestParameterValue =
              portletContainer.fixRequestParameterValue(parameterValues[0]);
          requestParameterMap.put(parameterName, fixedRequestParameterValue);
          logger.debug(
              "Found in ActionRequest: {0}=[{1}]", parameterName, fixedRequestParameterValue);
        }
      }

      UploadedFileFactory uploadedFileFactory =
          (UploadedFileFactory) BridgeFactoryFinder.getFactory(UploadedFileFactory.class);

      // Begin parsing the request for file parts:
      try {
        FileItemIterator fileItemIterator = null;

        if (clientDataRequest instanceof ResourceRequest) {
          ResourceRequest resourceRequest = (ResourceRequest) clientDataRequest;
          fileItemIterator =
              portletFileUpload.getItemIterator(new ActionRequestAdapter(resourceRequest));
        } else {
          ActionRequest actionRequest = (ActionRequest) clientDataRequest;
          fileItemIterator = portletFileUpload.getItemIterator(actionRequest);
        }

        boolean optimizeNamespace =
            BooleanHelper.toBoolean(
                bridgeContext.getInitParameter(
                    BridgeConfigConstants.PARAM_OPTIMIZE_PORTLET_NAMESPACE1),
                true);

        if (fileItemIterator != null) {

          int totalFiles = 0;

          // For each field found in the request:
          while (fileItemIterator.hasNext()) {

            try {
              totalFiles++;

              // Get the stream of field data from the request.
              FileItemStream fieldStream = (FileItemStream) fileItemIterator.next();

              // Get field name from the field stream.
              String fieldName = fieldStream.getFieldName();

              // If namespace optimization is enabled and the namespace is present in the field
              // name,
              // then remove the portlet namespace from the field name.
              if (optimizeNamespace) {
                int pos = fieldName.indexOf(namespace);

                if (pos >= 0) {
                  fieldName = fieldName.substring(pos + namespace.length());
                }
              }

              // Get the content-type, and file-name from the field stream.
              String contentType = fieldStream.getContentType();
              boolean formField = fieldStream.isFormField();

              String fileName = null;

              try {
                fileName = fieldStream.getName();
              } catch (InvalidFileNameException e) {
                fileName = e.getName();
              }

              // Copy the stream of file data to a temporary file. NOTE: This is necessary even if
              // the
              // current field is a simple form-field because the call below to
              // diskFileItem.getString()
              // will fail otherwise.
              DiskFileItem diskFileItem =
                  (DiskFileItem)
                      diskFileItemFactory.createItem(fieldName, contentType, formField, fileName);
              Streams.copy(fieldStream.openStream(), diskFileItem.getOutputStream(), true);

              // If the current field is a simple form-field, then save the form field value in the
              // map.
              if (diskFileItem.isFormField()) {
                String requestParameterValue =
                    diskFileItem.getString(clientDataRequest.getCharacterEncoding());
                String fixedRequestParameterValue =
                    portletContainer.fixRequestParameterValue(requestParameterValue);
                requestParameterMap.put(fieldName, fixedRequestParameterValue);
                logger.debug("{0}=[{1}]", fieldName, fixedRequestParameterValue);
              } else {

                File tempFile = diskFileItem.getStoreLocation();

                // If the copy was successful, then
                if (tempFile.exists()) {

                  // Copy the commons-fileupload temporary file to a file in the same temporary
                  // location, but with the filename provided by the user in the upload. This has
                  // two
                  // benefits: 1) The temporary file will have a nice meaningful name. 2) By copying
                  // the file, the developer can have access to a semi-permanent file, because the
                  // commmons-fileupload DiskFileItem.finalize() method automatically deletes the
                  // temporary one.
                  String tempFileName = tempFile.getName();
                  String tempFileAbsolutePath = tempFile.getAbsolutePath();

                  String copiedFileName = stripIllegalCharacters(fileName);

                  String copiedFileAbsolutePath =
                      tempFileAbsolutePath.replace(tempFileName, copiedFileName);
                  File copiedFile = new File(copiedFileAbsolutePath);
                  FileUtils.copyFile(tempFile, copiedFile);

                  // If present, build up a map of headers.
                  Map<String, List<String>> headersMap = new HashMap<String, List<String>>();
                  FileItemHeaders fileItemHeaders = fieldStream.getHeaders();

                  if (fileItemHeaders != null) {
                    Iterator<String> headerNameItr = fileItemHeaders.getHeaderNames();

                    if (headerNameItr != null) {

                      while (headerNameItr.hasNext()) {
                        String headerName = headerNameItr.next();
                        Iterator<String> headerValuesItr = fileItemHeaders.getHeaders(headerName);
                        List<String> headerValues = new ArrayList<String>();

                        if (headerValuesItr != null) {

                          while (headerValuesItr.hasNext()) {
                            String headerValue = headerValuesItr.next();
                            headerValues.add(headerValue);
                          }
                        }

                        headersMap.put(headerName, headerValues);
                      }
                    }
                  }

                  // Put a valid UploadedFile instance into the map that contains all of the
                  // uploaded file's attributes, along with a successful status.
                  Map<String, Object> attributeMap = new HashMap<String, Object>();
                  String id = Long.toString(((long) hashCode()) + System.currentTimeMillis());
                  String message = null;
                  UploadedFile uploadedFile =
                      uploadedFileFactory.getUploadedFile(
                          copiedFileAbsolutePath,
                          attributeMap,
                          diskFileItem.getCharSet(),
                          diskFileItem.getContentType(),
                          headersMap,
                          id,
                          message,
                          fileName,
                          diskFileItem.getSize(),
                          UploadedFile.Status.FILE_SAVED);

                  requestParameterMap.put(fieldName, copiedFileAbsolutePath);
                  addUploadedFile(fieldName, uploadedFile);
                  logger.debug(
                      "Received uploaded file fieldName=[{0}] fileName=[{1}]", fieldName, fileName);
                }
              }
            } catch (Exception e) {
              logger.error(e);

              UploadedFile uploadedFile = uploadedFileFactory.getUploadedFile(e);
              String fieldName = Integer.toString(totalFiles);
              addUploadedFile(fieldName, uploadedFile);
            }
          }
        }
      }

      // If there was an error in parsing the request for file parts, then put a bogus UploadedFile
      // instance in
      // the map so that the developer can have some idea that something went wrong.
      catch (Exception e) {
        logger.error(e);

        UploadedFile uploadedFile = uploadedFileFactory.getUploadedFile(e);
        addUploadedFile("unknown", uploadedFile);
      }

      clientDataRequest.setAttribute(PARAM_UPLOADED_FILES, requestParameterFileMap);

      // If not found in the request, Section 6.9 of the Bridge spec requires that the value of the
      // ResponseStateManager.RENDER_KIT_ID_PARAM request parameter be set to the value of the
      // "javax.portlet.faces.<portletName>.defaultRenderKitId" PortletContext attribute.
      String renderKitIdParam = requestParameterMap.get(ResponseStateManager.RENDER_KIT_ID_PARAM);

      if (renderKitIdParam == null) {
        renderKitIdParam = bridgeContext.getDefaultRenderKitId();

        if (renderKitIdParam != null) {
          requestParameterMap.put(ResponseStateManager.RENDER_KIT_ID_PARAM, renderKitIdParam);
        }
      }
    } catch (Exception e) {
      logger.error(e.getMessage(), e);
    }
  }