Package com.liferay.faces.bridge.container

Source Code of com.liferay.faces.bridge.container.PortletContainerImpl

/**
* Copyright (c) 2000-2014 Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* 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.
*/
package com.liferay.faces.bridge.container;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.portlet.ActionResponse;
import javax.portlet.BaseURL;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.MimeResponse;
import javax.portlet.PortalContext;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.PortletURL;
import javax.portlet.ResourceResponse;
import javax.portlet.ResourceURL;
import javax.portlet.faces.Bridge;
import javax.portlet.faces.Bridge.PortletPhase;

import com.liferay.faces.bridge.BridgeConstants;
import com.liferay.faces.bridge.config.BridgeConfig;
import com.liferay.faces.bridge.config.PortletConfigParam;
import com.liferay.faces.bridge.context.BridgeContext;
import com.liferay.faces.bridge.util.RequestParameter;
import com.liferay.faces.util.application.ResourceConstants;
import com.liferay.faces.util.helper.BooleanHelper;
import com.liferay.faces.util.lang.StringPool;
import com.liferay.faces.util.logging.Logger;
import com.liferay.faces.util.logging.LoggerFactory;


/**
* @author  Neil Griffin
*/
public class PortletContainerImpl extends PortletContainerCompatImpl {

  // serialVersionUID
  private static final long serialVersionUID = 293072155166625509L;

  // Logger
  private static final Logger logger = LoggerFactory.getLogger(PortletContainerImpl.class);

  /** Portlet-API request attribute that contains an instance of javax.portlet.PortletRequest */
  private static final String REQUEST_ATTR_PORTLET_REQUEST = "javax.portlet.request";

  /** Servlet-API request attribute that indicates the query part of the URL requested by the user-agent */
  private static final String REQUEST_ATTR_QUERY_STRING = "javax.servlet.forward.query_string";

  // Private Data Members
  private Boolean ableToSetHttpStatusCode;
  private BridgeConfig bridgeConfig;
  private boolean markupHeadElementSupported;
  private String requestQueryString;
  private String requestURL;
  private String responseNamespace;

  public PortletContainerImpl(PortletRequest portletRequest, BridgeConfig bridgeConfig) {
    String portalVendorClaim = portletRequest.getPortalContext().getProperty(
        PortalContext.MARKUP_HEAD_ELEMENT_SUPPORT);
    this.bridgeConfig = bridgeConfig;
    this.markupHeadElementSupported = (portalVendorClaim != null);
  }

  public void afterPhase(PhaseEvent phaseEvent) {
    // no-op
  }

  public void beforePhase(PhaseEvent event) {
    // no-op
  }

  public PortletURL createActionURL(String fromURL) throws MalformedURLException {

    try {
      logger.debug("createActionURL fromURL=[" + fromURL + "]");

      BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
      MimeResponse mimeResponse = (MimeResponse) bridgeContext.getPortletResponse();
      PortletURL actionURL = createActionURL(mimeResponse);
      copyRequestParameters(fromURL, actionURL);

      return actionURL;
    }
    catch (ClassCastException e) {
      throw new MalformedURLException(e.getMessage());
    }
  }

  public ResourceURL createPartialActionURL(String fromURL) throws MalformedURLException {
    logger.debug("createPartialActionURL fromURL=[" + fromURL + "]");

    return createResourceURL(fromURL);
  }

  /**
   * Note that this default method implementation doesn't help when a <redirect /> is present in the navigation-rule.
   * That's because the JSF implementation will end up calling this method during the Portlet 2.0 ACTION_PHASE, and
   * it's impossible for us to get a redirect URL (really, a render URL) from an ActionResponse. This method will need
   * to be overridden for each portlet container and handled in a container-dependent way.
   */
  public PortletURL createRedirectURL(String fromURL, Map<String, List<String>> parameters)
    throws MalformedURLException {

    PortletURL redirectURL = null;

    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
    PortletPhase portletRequestPhase = bridgeContext.getPortletRequestPhase();

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

      try {
        logger.debug("createRedirectURL fromURL=[" + fromURL + "]");

        MimeResponse mimeResponse = (MimeResponse) bridgeContext.getPortletResponse();
        redirectURL = mimeResponse.createRenderURL();
        copyRequestParameters(fromURL, redirectURL);

        if (parameters != null) {
          Set<String> parameterNames = parameters.keySet();

          for (String parameterName : parameterNames) {
            List<String> parameterValues = parameters.get(parameterName);
            String[] parameterValuesArray = parameterValues.toArray(new String[parameterValues.size()]);
            redirectURL.setParameter(parameterName, parameterValuesArray);
          }
        }
      }
      catch (ClassCastException e) {
        throw new MalformedURLException(e.getMessage());
      }
    }
    else {
      throw new UnsupportedOperationException("Unable to create a redirectURL (renderURL) during " +
        portletRequestPhase + " from URL=[" + fromURL + "]");
    }

    return redirectURL;
  }

  public PortletURL createRenderURL(String fromURL) throws MalformedURLException {
    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
    PortletPhase portletRequestPhase = bridgeContext.getPortletRequestPhase();

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

      try {
        logger.debug("createRenderURL fromURL=[" + fromURL + "]");

        MimeResponse mimeResponse = (MimeResponse) bridgeContext.getPortletResponse();
        PortletURL renderURL = createRenderURL(mimeResponse);
        copyRequestParameters(fromURL, renderURL);

        return renderURL;
      }
      catch (ClassCastException e) {
        throw new MalformedURLException(e.getMessage());
      }
    }
    else {
      throw new MalformedURLException("Unable to create a RenderURL during " + portletRequestPhase.toString());
    }

  }

  public ResourceURL createResourceURL(String fromURL) throws MalformedURLException {

    try {
      logger.debug("createResourceURL fromURL=[" + fromURL + "]");

      // Ask the portlet container to create a portlet resource URL.
      BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
      MimeResponse mimeResponse = (MimeResponse) bridgeContext.getPortletResponse();
      ResourceURL resourceURL = createResourceURL(mimeResponse);

      // If the "javax.faces.resource" token is found in the URL, then
      int tokenPos = fromURL.indexOf(ResourceConstants.JAVAX_FACES_RESOURCE);

      if (tokenPos >= 0) {

        // Parse-out the resourceId
        String resourceId = fromURL.substring(tokenPos);

        // Parse-out the resourceName and convert it to a URL parameter on the portlet resource URL.
        int queryStringPos = resourceId.indexOf('?');

        String resourceName = resourceId;

        if (queryStringPos > 0) {
          resourceName = resourceName.substring(0, queryStringPos);
        }

        int slashPos = resourceName.indexOf('/');

        if (slashPos > 0) {
          resourceName = resourceName.substring(slashPos + 1);
        }
        else {
          logger.error("There is no slash after the [{0}] token in resourceURL=[{1}]",
            ResourceConstants.JAVAX_FACES_RESOURCE, fromURL);
        }

        resourceURL.setParameter(ResourceConstants.JAVAX_FACES_RESOURCE, resourceName);
        logger.debug("Added parameter to portletURL name=[{0}] value=[{1}]",
          ResourceConstants.JAVAX_FACES_RESOURCE, resourceName);
      }

      // Copy the request parameters to the portlet resource URL.
      copyRequestParameters(fromURL, resourceURL);

      return resourceURL;
    }
    catch (ClassCastException e) {
      throw new MalformedURLException(e.getMessage());
    }
  }

  public String fixRequestParameterValue(String value) {

    // In this default implementation, there's nothing to fix, so just return the specified value.
    return value;
  }

  /**
   * FACES-1453: Since {@link EventResponse#setRenderParameters(EventRequest)} would end up clobbering existing
   * public/private render parameters, it is necessary to iterate through all of them and only maintain the ones that
   * don't already exist in {@link EventResponse#getRenderParameterMap()}.
   */
  public void maintainRenderParameters(EventRequest eventRequest, EventResponse eventResponse) {

    Map<String, String[]> existingResponseRenderParameterMap = eventResponse.getRenderParameterMap();

    // Maintain the public render parameters.
    Map<String, String[]> publicParameterMap = eventRequest.getPublicParameterMap();

    if (publicParameterMap != null) {

      Set<Entry<String, String[]>> entrySet = publicParameterMap.entrySet();

      for (Map.Entry<String, String[]> mapEntry : entrySet) {
        String key = mapEntry.getKey();

        boolean alreadyExists = false;

        if (existingResponseRenderParameterMap != null) {
          alreadyExists = existingResponseRenderParameterMap.containsKey(key);
        }

        if (alreadyExists) {

          // FACES-1453: Avoid clobbering existing public render parameters set on the EventResponse.
          if (logger.isTraceEnabled()) {
            String[] existingValues = existingResponseRenderParameterMap.get(key);

            logger.trace(
              "Not maintaining public render parameter name=[{0}] values=[{1}] because it already exists",
              key, existingValues);
          }
        }
        else {
          String[] values = mapEntry.getValue();
          eventResponse.setRenderParameter(key, values);
          logger.trace("Maintaining public render parameter name=[{0}] values=[{1}]", key, values);
        }
      }
    }

    // Maintain the private render parameters.
    Map<String, String[]> privateParameterMap = eventRequest.getPrivateParameterMap();

    if (privateParameterMap != null) {
      Set<Entry<String, String[]>> entrySet = privateParameterMap.entrySet();

      for (Map.Entry<String, String[]> mapEntry : entrySet) {
        String key = mapEntry.getKey();
        boolean alreadyExists = false;

        if (existingResponseRenderParameterMap != null) {
          alreadyExists = existingResponseRenderParameterMap.containsKey(key);
        }

        if (alreadyExists) {

          // FACES-1453: Avoid clobbering existing private render parameters set on the EventResponse.
          if (logger.isTraceEnabled()) {
            String[] existingValues = existingResponseRenderParameterMap.get(key);

            logger.trace(
              "Not maintaining private render parameter name=[{0}] values=[{1}] because it already exists",
              key, existingValues);
          }
        }
        else {
          String[] values = mapEntry.getValue();
          eventResponse.setRenderParameter(key, values);
          logger.trace("Maintaining private render parameter name=[{0}] values=[{1}]", key, values);
        }
      }
    }
  }

  public void redirect(String url) throws IOException {

    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
    PortletResponse portletResponse = bridgeContext.getPortletResponse();

    if (portletResponse instanceof ActionResponse) {
      ActionResponse actionResponse = (ActionResponse) portletResponse;
      actionResponse.sendRedirect(url);
    }
    else if (portletResponse instanceof ResourceResponse) {
      FacesContext facesContext = FacesContext.getCurrentInstance();

      if (isJSF2PartialRequest(facesContext)) {
        redirectJSF2PartialResponse(facesContext, (ResourceResponse) portletResponse, url);
      }
      else {
        throw new UnsupportedEncodingException(
          "Can only redirect during RESOURCE_PHASE if a JSF partial/Ajax request has been triggered");
      }
    }
    else {
      throw new UnsupportedEncodingException("Unable to redirect during " +
        bridgeContext.getPortletRequestPhase());
    }
  }

  /**
   * Copies any query paramters present in the specified "from" URL to the specified "to" URL.
   *
   * @param   fromURL  The String-based URL to copy query parameters from.
   * @param   toURL    The portlet-based URL to copy query parameters to.
   *
   * @throws  MalformedURLException
   */
  protected void copyRequestParameters(String fromURL, BaseURL toURL) throws MalformedURLException {
    List<RequestParameter> requestParameters = parseRequestParameters(fromURL);

    if (requestParameters != null) {

      for (RequestParameter requestParameter : requestParameters) {
        String name = requestParameter.getName();
        String value = requestParameter.getValue();
        toURL.setParameter(name, value);
        logger.debug("Copied parameter to portletURL name=[{0}] value=[{1}]", name, value);
      }
    }
  }

  protected PortletURL createActionURL(MimeResponse mimeResponse) {
    return mimeResponse.createActionURL();
  }

  protected PortletURL createRenderURL(MimeResponse mimeResponse) {
    return mimeResponse.createRenderURL();
  }

  protected ResourceURL createResourceURL(MimeResponse mimeResponse) {
    return mimeResponse.createResourceURL();
  }

  /**
   * Parses the specified URL and returns a list of query parameters that are found.
   *
   * @param   url  The URL to parse.
   *
   * @return  The list of query parameters found.
   *
   * @throws  MalformedURLException
   */
  protected List<RequestParameter> parseRequestParameters(String url) throws MalformedURLException {

    List<RequestParameter> requestParameters = null;

    if (url != null) {
      int pos = url.indexOf("?");

      if (pos >= 0) {
        String queryString = url.substring(pos + 1);

        if ((queryString != null) && (queryString.length() > 0)) {
          requestParameters = new ArrayList<RequestParameter>();

          String[] queryParameters = queryString.split(BridgeConstants.REGEX_AMPERSAND_DELIMITER);

          for (String queryParameter : queryParameters) {
            String[] nameValueArray = queryParameter.split("[=]");

            if (nameValueArray != null) {

              if (nameValueArray.length == 2) {
                String name = nameValueArray[0];
                String value = nameValueArray[1];
                requestParameters.add(new RequestParameter(name, value));
              }
              else {
                throw new MalformedURLException("invalid name/value pair: " + queryParameter);
              }
            }
          }
        }
      }
    }

    return requestParameters;
  }

  protected boolean getContextParamAbleToSetHttpStatusCode(boolean defaultValue) {

    String contextParamValue = bridgeConfig.getContextParameter(PortletConfigParam.ContainerAbleToSetHttpStatusCode
        .getName());

    if (contextParamValue == null) {
      contextParamValue = bridgeConfig.getContextParameter(PortletConfigParam.ContainerAbleToSetHttpStatusCode
          .getAlternateName());
    }

    return BooleanHelper.toBoolean(contextParamValue, defaultValue);
  }

  public boolean isAbleToAddScriptResourceToHead() {
    return isMarkupHeadElementSupported();
  }

  public boolean isAbleToAddScriptTextToHead() {
    return isMarkupHeadElementSupported();
  }

  public boolean isAbleToAddStyleSheetResourceToHead() {
    return isMarkupHeadElementSupported();
  }

  public boolean isPostRedirectGetSupported() {
    return true;
  }

  /**
   * Determines whether or not the portlet container supports the standard Portlet 2.0 mechanism for adding resources
   * to the <head>...</head> section of the rendered portal page. Section PLT.12.5.4 of the Portlet 2.0 spec indicates
   * that this is an "optional" feature for vendors to implement.
   *
   * @return  True if the portlet container supports the standard Portlet 2.0 mechanism for adding resources.
   */
  protected boolean isMarkupHeadElementSupported() {
    return markupHeadElementSupported;
  }

  public boolean isAbleToSetHttpStatusCode() {

    if (ableToSetHttpStatusCode == null) {

      // Although it's not the most performant option, it's safest to assume that the portlet container has not
      // implemented this feature. That way, the ResourceHandlerImpl will always deliver stuff like jsf.js back
      // to the browser. As we support more portlet containers in the future (Pluto, etc.) we can create
      // implementations that override this.
      boolean defaultValue = false;
      ableToSetHttpStatusCode = getContextParamAbleToSetHttpStatusCode(defaultValue);
    }

    return ableToSetHttpStatusCode;
  }

  public boolean isAbleToSetResourceResponseBufferSize() {
    return true;
  }

  public boolean isAbleToForwardOnDispatch() {
    return true;
  }

  public String[] getHeader(String name) {
    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
    PortletRequest portletRequest = bridgeContext.getPortletRequest();
    Enumeration<String> properties = portletRequest.getProperties(name);
    List<String> propertyList = new ArrayList<String>();

    while (properties.hasMoreElements()) {
      propertyList.add(properties.nextElement());
    }

    return propertyList.toArray(new String[propertyList.size()]);
  }

  public long getHttpServletRequestDateHeader(String name) {

    // Unsupported by default implementation.
    return -1L;
  }

  public void setMimeResponseContentType(MimeResponse mimeResponse, String contentType) {
    mimeResponse.setContentType(contentType);
  }

  public PhaseId getPhaseId() {

    // no-op
    return null;
  }

  public String getRequestParameter(String name) {
    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();

    return fixRequestParameterValue(bridgeContext.getPortletRequest().getParameter(name));
  }

  public String[] getRequestParameterValues(String name) {
    String[] values = null;
    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
    String[] originalValues = bridgeContext.getPortletRequest().getParameterValues(name);

    if (originalValues != null) {
      values = new String[originalValues.length];

      for (int i = 0; i < originalValues.length; i++) {
        values[i] = fixRequestParameterValue(originalValues[i]);
      }
    }

    return values;
  }

  public String getRequestQueryString() {

    if (requestQueryString == null) {
      BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
      PortletRequest portletRequest = bridgeContext.getPortletRequest();
      requestQueryString = (String) portletRequest.getAttribute(REQUEST_ATTR_QUERY_STRING);

      if (requestQueryString == null) {

        // Some portlet bridges wrap the portal's PortletRequest implementation instance (which prevents us from
        // getting the query_string). As a workaround, we might still be able to get  the original
        // PortletRequest instance, because the Portlet spec says it must be stored in the
        // "javax.portlet.request" attribute.
        Object portletRequestAsObject = portletRequest.getAttribute(REQUEST_ATTR_PORTLET_REQUEST);

        if ((portletRequestAsObject != null) && (portletRequestAsObject instanceof PortletRequest)) {
          portletRequest = (PortletRequest) portletRequestAsObject;
          requestQueryString = (String) portletRequest.getAttribute(REQUEST_ATTR_QUERY_STRING);
        }
      }
    }

    return requestQueryString;
  }

  public String getRequestURL() {

    if (requestURL == null) {

      // Note that this is an approximation (best guess) of the original URL.
      StringBuilder buf = new StringBuilder();
      BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
      PortletRequest portletRequest = bridgeContext.getPortletRequest();
      buf.append(portletRequest.getScheme());
      buf.append(StringPool.COLON);
      buf.append(StringPool.FORWARD_SLASH);
      buf.append(StringPool.FORWARD_SLASH);
      buf.append(portletRequest.getServerName());
      buf.append(StringPool.COLON);
      buf.append(portletRequest.getServerPort());
      buf.append(portletRequest.getContextPath());
      buf.append(StringPool.QUESTION);
      buf.append(getRequestQueryString());
      requestURL = buf.toString();
    }

    return requestURL;
  }

  public String getResponseNamespace() {

    if (responseNamespace == null) {

      BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
      responseNamespace = bridgeContext.getPortletResponse().getNamespace();

      if (BridgeConstants.WSRP_REWRITE.equals(responseNamespace)) {
        responseNamespace = bridgeContext.getPortletConfig().getPortletName() +
          bridgeContext.getPortletContext().getPortletContextName();
      }
    }

    return responseNamespace;
  }

  public boolean isNamespacedParameters() {
    return false;
  }
}
TOP

Related Classes of com.liferay.faces.bridge.container.PortletContainerImpl

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.