Permalink
Browse files

FACES-2976 Namespaced ViewState and other parameters cannot be obtain…

…ed via portletRequest.getParameter() (fixed javax.faces.ViewState parameter regression revealed by Bridge TCK Test getRequestParameterMapCoreTest)
  • Loading branch information...
1 parent 4ff7454 commit de95886b446608ecee985aacf85835e43d85962a @ngriffin7a ngriffin7a committed Feb 10, 2017
@@ -22,18 +22,19 @@
import java.util.Map;
import java.util.Set;
-import javax.faces.component.UINamingContainer;
-import javax.faces.context.FacesContext;
import javax.portlet.ClientDataRequest;
+import javax.portlet.PortalContext;
import javax.portlet.PortletConfig;
import javax.portlet.PortletContext;
import javax.portlet.PortletRequest;
import javax.portlet.PortletSession;
import javax.servlet.ServletContext;
+import com.liferay.faces.bridge.context.BridgePortalContext;
import com.liferay.faces.bridge.model.UploadedFile;
import com.liferay.faces.bridge.model.internal.UploadedFileBridgeImpl;
import com.liferay.faces.bridge.scope.internal.BridgeRequestScope;
+import com.liferay.faces.bridge.util.internal.FacesRuntimeUtil;
import com.liferay.faces.util.context.map.FacesRequestParameterMap;
import com.liferay.faces.util.context.map.MultiPartFormData;
import com.liferay.faces.util.product.Product;
@@ -169,6 +170,12 @@ protected FacesRequestParameterMap getFacesRequestParameterMap(PortletRequest po
FacesRequestParameterMap facesRequestParameterMap = null;
Map<String, String> facesViewParameterMap = getFacesViewParameterMap(facesViewQueryString);
+ PortalContext portalContext = portletRequest.getPortalContext();
+ String strictNamespacedParametersSupport = portalContext.getProperty(
+ BridgePortalContext.STRICT_NAMESPACED_PARAMETERS_SUPPORT);
+ boolean strictParameterNamespacing = (strictNamespacedParametersSupport != null);
+ boolean namespaceViewState = strictParameterNamespacing && FacesRuntimeUtil.isNamespacedViewStateSupported();
+
if (portletRequest instanceof ClientDataRequest) {
ClientDataRequest clientDataRequest = (ClientDataRequest) portletRequest;
@@ -182,7 +189,7 @@ protected FacesRequestParameterMap getFacesRequestParameterMap(PortletRequest po
if (multiPartFormData == null) {
facesRequestParameterMap = new FacesRequestParameterMapImpl(responseNamespace, bridgeRequestScope,
- facesViewParameterMap, defaultRenderKitId, getSeparatorChar());
+ facesViewParameterMap, defaultRenderKitId, getSeparatorChar(), strictParameterNamespacing, namespaceViewState);
MultiPartFormDataProcessor multiPartFormDataProcessor = new MultiPartFormDataProcessorImpl();
Map<String, List<com.liferay.faces.util.model.UploadedFile>> uploadedFileMap =
@@ -203,7 +210,8 @@ protected FacesRequestParameterMap getFacesRequestParameterMap(PortletRequest po
if (facesRequestParameterMap == null) {
Map<String, String[]> parameterMap = portletRequest.getParameterMap();
facesRequestParameterMap = new FacesRequestParameterMapImpl(parameterMap, responseNamespace,
- bridgeRequestScope, facesViewParameterMap, defaultRenderKitId, getSeparatorChar());
+ bridgeRequestScope, facesViewParameterMap, defaultRenderKitId, getSeparatorChar(),
+ strictParameterNamespacing, namespaceViewState);
}
return facesRequestParameterMap;
@@ -51,26 +51,31 @@
private String defaultRenderKitId;
private Map<String, String> facesViewParameterMap;
private String namespace;
+ private boolean namespaceViewState;
private String separatorChar;
private boolean separatorCharEnabled;
+ private boolean strictParameterNamespacing;
private Map<String, String[]> wrappedParameterMap;
public FacesRequestParameterMapImpl(String namespace, BridgeRequestScope bridgeRequestScope,
- Map<String, String> facesViewParameterMap, String defaultRenderKitId, String separatorChar) {
+ Map<String, String> facesViewParameterMap, String defaultRenderKitId, String separatorChar,
+ boolean strictParameterNamespacing, boolean namespaceViewState) {
this(new HashMap<String, String[]>(), namespace, bridgeRequestScope, facesViewParameterMap, defaultRenderKitId,
- separatorChar);
+ separatorChar, strictParameterNamespacing, namespaceViewState);
}
public FacesRequestParameterMapImpl(Map<String, String[]> parameterMap, String namespace,
BridgeRequestScope bridgeRequestScope, Map<String, String> facesViewParameterMap, String defaultRenderKitId,
- String separatorChar) {
+ String separatorChar, boolean strictParameterNamespacing, boolean namespaceViewState) {
this.wrappedParameterMap = parameterMap;
this.namespace = namespace;
this.bridgeRequestScope = bridgeRequestScope;
this.facesViewParameterMap = facesViewParameterMap;
this.defaultRenderKitId = defaultRenderKitId;
this.separatorChar = separatorChar;
this.separatorCharEnabled = ((separatorChar != null) && (separatorChar.length() > 0));
+ this.strictParameterNamespacing = strictParameterNamespacing;
+ this.namespaceViewState = namespaceViewState;
}
@Override
@@ -92,7 +97,13 @@ public void addValue(String key, String value) {
wrappedParameterMap.put(key, values);
}
else {
- wrappedParameterMap.put(namespace + key, values);
+
+ if (strictParameterNamespacing) {
+ wrappedParameterMap.put(namespace + key, values);
+ }
+ else {
+ wrappedParameterMap.put(key, values);
+ }
}
}
@@ -312,7 +323,13 @@ public boolean isEmpty() {
requestParameterNameList.add(namespace + separatorChar + ResponseStateManager.VIEW_STATE_PARAM);
}
else {
- requestParameterNameList.add(namespace + ResponseStateManager.VIEW_STATE_PARAM);
+
+ if (namespaceViewState) {
+ requestParameterNameList.add(namespace + ResponseStateManager.VIEW_STATE_PARAM);
+ }
+ else {
+ requestParameterNameList.add(ResponseStateManager.VIEW_STATE_PARAM);
+ }
}
}
@@ -0,0 +1,136 @@
+/**
+ * Copyright (c) 2000-2016 Liferay, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.liferay.faces.bridge.renderkit.bridge.internal;
+
+import java.io.IOException;
+
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.PartialResponseWriter;
+import javax.faces.context.ResponseWriterWrapper;
+import javax.portlet.PortalContext;
+import javax.portlet.PortletRequest;
+
+import com.liferay.faces.bridge.context.BridgePortalContext;
+import com.liferay.faces.bridge.util.internal.FacesRuntimeUtil;
+import com.liferay.faces.util.logging.Logger;
+import com.liferay.faces.util.logging.LoggerFactory;
+
+
+/**
+ * @author Neil Griffin
+ */
+public abstract class ResponseWriterBridgeCompat_2_0_Impl extends ResponseWriterWrapper {
+
+ // Logger
+ private static final Logger logger = LoggerFactory.getLogger(ResponseWriterBridgeCompat_2_0_Impl.class);
+
+ // Protected Constants
+ protected static final String ATTRIBUTE_AUTOCOMPLETE = "autocomplete";
+ protected static final String DOCTYPE_MARKER = "<!DOCTYPE";
+ protected static final String VALUE_OFF = "off";
+ protected static final String VIEW_STATE_MARKER = PartialResponseWriter.VIEW_STATE_MARKER;
+ protected static final String XML_MARKER = "<?xml";
+
+ // Protected Data Members
+ protected boolean namespacedParameters;
+
+ public ResponseWriterBridgeCompat_2_0_Impl() {
+
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ ExternalContext externalContext = facesContext.getExternalContext();
+ PortletRequest portletRequest = (PortletRequest) externalContext.getRequest();
+ PortalContext portalContext = portletRequest.getPortalContext();
+ String namespacedParametersSupport = portalContext.getProperty(
+ BridgePortalContext.STRICT_NAMESPACED_PARAMETERS_SUPPORT);
+ this.namespacedParameters = (namespacedParametersSupport != null) &&
+ FacesRuntimeUtil.isNamespacedViewStateSupported();
+ }
+
+ /**
+ * <p>The main purpose of this method is to solve the jsf.js limitation #1 as described in the class header
+ * comments.</p>
+ *
+ * <p>The Mojarra JSF implementation has a vendor-specific com.sun.faces.facelets.compiler.UIInstructions class that
+ * will render the following markers:</p>
+ *
+ * <ul>
+ * <li>&lt;?xml version="1.0" encoding="UTF-8"?&gt;</li>
+ * <li>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;</li>
+ * </ul>
+ *
+ * <p>This method will ensure that such markers are not rendered to the response, as they should not be rendered as
+ * part of a portlet, since portlets are simply HTML fragment that are aggregated together into a single HTML
+ * document by the portlet container.</p>
+ */
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+
+ if (len > 0) {
+
+ String data = new String(cbuf, off, len);
+
+ if (data.startsWith(XML_MARKER) || data.startsWith(DOCTYPE_MARKER)) {
+
+ logger.trace("filtering marker");
+
+ int greaterThanPos = data.indexOf(">");
+
+ if (greaterThanPos > 0) {
+ len -= (greaterThanPos + 1);
+ off += (greaterThanPos + 1);
+ }
+ }
+
+ if (len > 0) {
+
+ if (logger.isTraceEnabled()) {
+ String value = new String(cbuf, off, len);
+ logger.trace("writing value=[{0}]", value);
+ }
+
+ getWrapped().write(cbuf, off, len);
+ }
+ }
+ }
+
+ protected void writeViewStateHiddenField() throws IOException {
+
+ startElement("input", null);
+ writeAttribute("type", "hidden", null);
+
+ String viewStateName = PartialResponseWriter.VIEW_STATE_MARKER;
+
+ if (namespacedParameters) {
+
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ String namingContainerId = facesContext.getViewRoot().getContainerClientId(facesContext);
+ viewStateName = namingContainerId + viewStateName;
+ }
+
+ writeAttribute("name", viewStateName, null);
+
+ // TODO: The following line is a workaround and needs to be fixed in FACES-1797.
+ writeAttribute("id", viewStateName, null);
+
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ String viewState = facesContext.getApplication().getStateManager().getViewState(facesContext);
+ writeAttribute("value", viewState, null);
+ writeAttribute(ATTRIBUTE_AUTOCOMPLETE, VALUE_OFF, null);
+ endElement("input");
+ }
+}
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2000-2016 Liferay, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.liferay.faces.bridge.util.internal;
+
+import javax.faces.render.ResponseStateManager;
+
+import com.liferay.faces.util.logging.Logger;
+import com.liferay.faces.util.logging.LoggerFactory;
+import com.liferay.faces.util.product.Product;
+import com.liferay.faces.util.product.ProductFactory;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class FacesRuntimeUtil {
+
+ // Logger
+ private static final Logger logger = LoggerFactory.getLogger(FacesRuntimeUtil.class);
+
+ // Private Constants
+ private static final boolean JSF_RUNTIME_SUPPORTS_NAMESPACING_VIEWSTATE;
+
+ static {
+ boolean namespacedViewStateSupported = false;
+ Product mojarra = ProductFactory.getProduct(Product.Name.MOJARRA);
+
+ if (mojarra.isDetected()) {
+
+ int mojarraMajorVersion = mojarra.getMajorVersion();
+
+ if (mojarraMajorVersion == 2) {
+
+ int mojarraMinorVersion = mojarra.getMinorVersion();
+
+ if (mojarraMinorVersion == 1) {
+ namespacedViewStateSupported = (mojarra.getPatchVersion() >= 27);
+ }
+ else if (mojarraMinorVersion == 2) {
+ namespacedViewStateSupported = (mojarra.getPatchVersion() >= 4);
+ }
+ else if (mojarraMinorVersion > 2) {
+ namespacedViewStateSupported = true;
+ }
+ }
+ else if (mojarraMajorVersion > 2) {
+ namespacedViewStateSupported = true;
+ }
+ }
+
+ Product jsf = ProductFactory.getProduct(Product.Name.JSF);
+ logger.debug("JSF runtime [{0}] version [{1}].[{2}].[{3}] supports namespacing [{4}]: [{5}]", jsf.getTitle(),
+ jsf.getMajorVersion(), jsf.getMinorVersion(), jsf.getPatchVersion(), ResponseStateManager.VIEW_STATE_PARAM,
+ namespacedViewStateSupported);
+
+ JSF_RUNTIME_SUPPORTS_NAMESPACING_VIEWSTATE = namespacedViewStateSupported;
+ }
+
+ public static boolean isNamespacedViewStateSupported() {
+ return JSF_RUNTIME_SUPPORTS_NAMESPACING_VIEWSTATE;
+ }
+}

0 comments on commit de95886

Please sign in to comment.