From 6af76972060947404c6031bc3adfbf0f8bd7cf7a Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 18 Jan 2025 09:58:08 -0400 Subject: [PATCH 1/5] IT #5543 --- test/issue5543/pom.xml | 41 ++++++++++++ .../mojarra/test/issue5543/Issue5543Bean.java | 34 ++++++++++ .../issue5543/src/main/webapp/WEB-INF/web.xml | 34 ++++++++++ .../issue5543/src/main/webapp/issue5543.xhtml | 35 ++++++++++ .../mojarra/test/issue5543/Issue5543IT.java | 64 +++++++++++++++++++ test/pom.xml | 1 + 6 files changed, 209 insertions(+) create mode 100644 test/issue5543/pom.xml create mode 100644 test/issue5543/src/main/java/org/eclipse/mojarra/test/issue5543/Issue5543Bean.java create mode 100644 test/issue5543/src/main/webapp/WEB-INF/web.xml create mode 100644 test/issue5543/src/main/webapp/issue5543.xhtml create mode 100644 test/issue5543/src/test/java/org/eclipse/mojarra/test/issue5543/Issue5543IT.java diff --git a/test/issue5543/pom.xml b/test/issue5543/pom.xml new file mode 100644 index 0000000000..afd776a7cf --- /dev/null +++ b/test/issue5543/pom.xml @@ -0,0 +1,41 @@ + + + + 4.0.0 + + + org.eclipse.mojarra.test + pom + 4.0.10-SNAPSHOT + + + issue5543 + war + + Mojarra ${project.version} - INTEGRATION TESTS - ${project.artifactId} + + + + org.eclipse.mojarra.test + base + ${project.version} + test + + + diff --git a/test/issue5543/src/main/java/org/eclipse/mojarra/test/issue5543/Issue5543Bean.java b/test/issue5543/src/main/java/org/eclipse/mojarra/test/issue5543/Issue5543Bean.java new file mode 100644 index 0000000000..ed450600c6 --- /dev/null +++ b/test/issue5543/src/main/java/org/eclipse/mojarra/test/issue5543/Issue5543Bean.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GPL-2.0 with Classpath-exception-2.0 which + * is available at https://openjdk.java.net/legal/gplv2+ce.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 or Apache-2.0 + */ +package org.eclipse.mojarra.test.issue5543; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Named; + +@Named +@RequestScoped +public class Issue5543Bean { + + private String text = "ä"; + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } +} diff --git a/test/issue5543/src/main/webapp/WEB-INF/web.xml b/test/issue5543/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..8784690178 --- /dev/null +++ b/test/issue5543/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,34 @@ + + + + + facesServlet + jakarta.faces.webapp.FacesServlet + 1 + + + facesServlet + *.xhtml + + \ No newline at end of file diff --git a/test/issue5543/src/main/webapp/issue5543.xhtml b/test/issue5543/src/main/webapp/issue5543.xhtml new file mode 100644 index 0000000000..a5683d231b --- /dev/null +++ b/test/issue5543/src/main/webapp/issue5543.xhtml @@ -0,0 +1,35 @@ + + + + + issue5543 + + + + + + + + + + diff --git a/test/issue5543/src/test/java/org/eclipse/mojarra/test/issue5543/Issue5543IT.java b/test/issue5543/src/test/java/org/eclipse/mojarra/test/issue5543/Issue5543IT.java new file mode 100644 index 0000000000..8d26615051 --- /dev/null +++ b/test/issue5543/src/test/java/org/eclipse/mojarra/test/issue5543/Issue5543IT.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GPL-2.0 with Classpath-exception-2.0 which + * is available at https://openjdk.java.net/legal/gplv2+ce.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 or Apache-2.0 + */ +package org.eclipse.mojarra.test.issue5543; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.eclipse.mojarra.test.base.BaseIT; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +class Issue5543IT extends BaseIT { + + @FindBy(id = "form:input") + private WebElement input; + + @FindBy(id = "form:nonAjaxSubmit") + private WebElement nonAjaxSubmit; + + @FindBy(id = "form:ajaxSubmit") + private WebElement ajaxSubmit; + + @FindBy(id = "form:output") + private WebElement output; + + /** + * https://github.com/eclipse-ee4j/mojarra/issues/5543 + */ + @Test + void testDefaultResponseEncodingNonAjax() { + open("issue5543.xhtml"); + assertEquals("ä", input.getAttribute("value")); + assertEquals("ä", output.getText()); + guardHttp(nonAjaxSubmit::click); + assertEquals("ä", input.getAttribute("value")); + assertEquals("ä", output.getText()); + } + + /** + * https://github.com/eclipse-ee4j/mojarra/issues/5543 + */ + @Test + void testDefaultResponseEncodingAjax() { + open("issue5543.xhtml"); + assertEquals("ä", input.getAttribute("value")); + assertEquals("ä", output.getText()); + guardHttp(ajaxSubmit::click); + assertEquals("ä", input.getAttribute("value")); + assertEquals("ä", output.getText()); + } +} diff --git a/test/pom.xml b/test/pom.xml index a0935dcdfe..dcd4efb4dc 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -41,6 +41,7 @@ issue5503 issue5507 issue5511 + issue5543 From a30bdf8371f650a5291cffcd44c05052a4bc4dfb Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 18 Jan 2025 10:01:14 -0400 Subject: [PATCH 2/5] Fix #5543 --- .../view/FaceletViewHandlingStrategy.java | 47 ++++++++++--------- .../main/java/com/sun/faces/util/Util.java | 18 +++---- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java b/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java index 3b0dd8052d..33d77a23a7 100644 --- a/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java +++ b/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java @@ -43,6 +43,7 @@ import static jakarta.faces.application.Resource.COMPONENT_RESOURCE_KEY; import static jakarta.faces.application.StateManager.IS_BUILDING_INITIAL_STATE; import static jakarta.faces.application.StateManager.STATE_SAVING_METHOD_SERVER; +import static jakarta.faces.application.ViewHandler.CHARACTER_ENCODING_KEY; import static jakarta.faces.application.ViewHandler.DEFAULT_FACELETS_SUFFIX; import static jakarta.faces.application.ViewVisitOption.RETURN_AS_MINIMAL_IMPLICIT_OUTCOME; import static jakarta.faces.component.UIComponent.BEANINFO_KEY; @@ -57,7 +58,6 @@ import static java.util.Collections.emptyList; import static java.util.logging.Level.FINE; import static java.util.logging.Level.FINEST; -import static java.util.logging.Level.SEVERE; import static java.util.logging.Level.WARNING; import java.beans.BeanDescriptor; @@ -78,26 +78,6 @@ import java.util.logging.Logger; import java.util.stream.Stream; -import com.sun.faces.application.ApplicationAssociate; -import com.sun.faces.config.WebConfiguration; -import com.sun.faces.context.StateContext; -import com.sun.faces.facelets.compiler.FaceletDoctype; -import com.sun.faces.facelets.el.ContextualCompositeMethodExpression; -import com.sun.faces.facelets.el.VariableMapperWrapper; -import com.sun.faces.facelets.impl.DefaultFaceletFactory; -import com.sun.faces.facelets.impl.XMLFrontMatterSaver; -import com.sun.faces.facelets.tag.composite.CompositeComponentBeanInfo; -import com.sun.faces.facelets.tag.faces.CompositeComponentTagHandler; -import com.sun.faces.facelets.tag.ui.UIDebug; -import com.sun.faces.renderkit.RenderKitUtils; -import com.sun.faces.renderkit.html_basic.DoctypeRenderer; -import com.sun.faces.util.Cache; -import com.sun.faces.util.ComponentStruct; -import com.sun.faces.util.FacesLogger; -import com.sun.faces.util.HtmlUtils; -import com.sun.faces.util.RequestStateManager; -import com.sun.faces.util.Util; - import jakarta.el.ELContext; import jakarta.el.ExpressionFactory; import jakarta.el.MethodExpression; @@ -145,6 +125,26 @@ import jakarta.faces.view.facelets.FaceletContext; import jakarta.servlet.http.HttpSession; +import com.sun.faces.application.ApplicationAssociate; +import com.sun.faces.config.WebConfiguration; +import com.sun.faces.context.StateContext; +import com.sun.faces.facelets.compiler.FaceletDoctype; +import com.sun.faces.facelets.el.ContextualCompositeMethodExpression; +import com.sun.faces.facelets.el.VariableMapperWrapper; +import com.sun.faces.facelets.impl.DefaultFaceletFactory; +import com.sun.faces.facelets.impl.XMLFrontMatterSaver; +import com.sun.faces.facelets.tag.composite.CompositeComponentBeanInfo; +import com.sun.faces.facelets.tag.faces.CompositeComponentTagHandler; +import com.sun.faces.facelets.tag.ui.UIDebug; +import com.sun.faces.renderkit.RenderKitUtils; +import com.sun.faces.renderkit.html_basic.DoctypeRenderer; +import com.sun.faces.util.Cache; +import com.sun.faces.util.ComponentStruct; +import com.sun.faces.util.FacesLogger; +import com.sun.faces.util.HtmlUtils; +import com.sun.faces.util.RequestStateManager; +import com.sun.faces.util.Util; + /** * This {@link ViewHandlingStrategy} handles Facelets/PDL-based views. */ @@ -929,6 +929,11 @@ protected ResponseWriter createResponseWriter(FacesContext context) throws IOExc // Save encoding in UIViewRoot for faster consult when Util#getResponseEncoding() is invoked again elsewhere. context.getViewRoot().getAttributes().put(FACELETS_ENCODING_KEY, encoding); + // Save encoding in Session as per spec section "2.5.2.2. Determining the Character Encoding". + if (context.getExternalContext().getSession(false) != null) { + context.getExternalContext().getSessionMap().put(CHARACTER_ENCODING_KEY, encoding); + } + // Now, clone with the real writer ResponseWriter writer = initWriter.cloneWithWriter(extContext.getResponseOutputWriter()); diff --git a/impl/src/main/java/com/sun/faces/util/Util.java b/impl/src/main/java/com/sun/faces/util/Util.java index ca481492aa..dc2aef2296 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -77,13 +77,6 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import com.sun.faces.RIConstants; -import com.sun.faces.application.ApplicationAssociate; -import com.sun.faces.config.WebConfiguration; -import com.sun.faces.config.manager.FacesSchema; -import com.sun.faces.facelets.component.UIRepeat; -import com.sun.faces.io.FastStringWriter; - import jakarta.el.ELResolver; import jakarta.el.ValueExpression; import jakarta.enterprise.inject.spi.BeanManager; @@ -111,6 +104,13 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.MappingMatch; +import com.sun.faces.RIConstants; +import com.sun.faces.application.ApplicationAssociate; +import com.sun.faces.config.WebConfiguration; +import com.sun.faces.config.manager.FacesSchema; +import com.sun.faces.facelets.component.UIRepeat; +import com.sun.faces.io.FastStringWriter; + /** * Util is a class ... * @@ -1666,8 +1666,8 @@ public static String getResponseEncoding(FacesContext context, Optional } if (encoding == null && context.getExternalContext().getSession(false) != null) { - // 4. If still none found then get previously known request encoding from session. - // See also ViewHandler#initView(). + // 4. If still none found then get previously known request or response encoding from session. + // See also ViewHandler#initView() and FaceletViewHandlingStrategy#createResponseWriter(). encoding = (String) context.getExternalContext().getSessionMap().get(CHARACTER_ENCODING_KEY); if (encoding != null && LOGGER.isLoggable(FINEST)) { From 81b1912338fb67452551acc3546c212d66a161f5 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 18 Jan 2025 10:16:28 -0400 Subject: [PATCH 3/5] Improved #5543; drop pesky Optional and improved code comments --- .../view/FaceletViewHandlingStrategy.java | 24 +++++++++---------- .../main/java/com/sun/faces/util/Util.java | 8 +++---- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java b/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java index 33d77a23a7..e64ec2198d 100644 --- a/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java +++ b/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java @@ -71,7 +71,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; @@ -905,19 +904,20 @@ protected ResponseWriter createResponseWriter(FacesContext context) throws IOExc } } - // get our content type - String contentType = (String) context.getAttributes().get("facelets.ContentType"); + // Get the as default content type. + // See also ViewHandler#apply(). + String defaultContentType = (String) context.getAttributes().get("facelets.ContentType"); - // get the encoding - String encoding = (String) context.getAttributes().get(FACELETS_ENCODING_KEY); + // Get the or otherwise Facelets default encoding of UTF-8 as default encoding. + // See also SAXCompiler#doCompile() and EncodingHandler#apply(). + String defaultEncoding = (String) context.getAttributes().get(FACELETS_ENCODING_KEY); - // Create a dummy ResponseWriter with a bogus writer, - // so we can figure out what content type and encoding the ReponseWriter - // is really going to ask for - ResponseWriter initWriter = renderKit.createResponseWriter(NullWriter.INSTANCE, contentType, encoding); + // Create a dummy ResponseWriter with a bogus writer, so we can figure out what + // content type and encoding the ResponseWriter is ultimately going to need. + ResponseWriter initWriter = renderKit.createResponseWriter(NullWriter.INSTANCE, defaultContentType, defaultEncoding); - contentType = getResponseContentType(context, initWriter.getContentType()); - encoding = Util.getResponseEncoding(context, Optional.ofNullable(initWriter.getCharacterEncoding())); + String contentType = getResponseContentType(context, initWriter.getContentType()); + String encoding = Util.getResponseEncoding(context, initWriter.getCharacterEncoding()); // apply them to the response char[] buffer = new char[1028]; @@ -929,7 +929,7 @@ protected ResponseWriter createResponseWriter(FacesContext context) throws IOExc // Save encoding in UIViewRoot for faster consult when Util#getResponseEncoding() is invoked again elsewhere. context.getViewRoot().getAttributes().put(FACELETS_ENCODING_KEY, encoding); - // Save encoding in Session as per spec section "2.5.2.2. Determining the Character Encoding". + // Save encoding in Session for consult in subsequent postback request as per spec section "2.5.2.2. Determining the Character Encoding". if (context.getExternalContext().getSession(false) != null) { context.getExternalContext().getSessionMap().put(CHARACTER_ENCODING_KEY, encoding); } diff --git a/impl/src/main/java/com/sun/faces/util/Util.java b/impl/src/main/java/com/sun/faces/util/Util.java index dc2aef2296..feebf087f3 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -1626,7 +1626,7 @@ public static int extractFirstNumericSegment(String clientId, char separatorChar * @return the encoding to be used for the response */ public static String getResponseEncoding(FacesContext context) { - return getResponseEncoding(context, Optional.empty()); + return getResponseEncoding(context, null); } /** @@ -1634,7 +1634,7 @@ public static String getResponseEncoding(FacesContext context) { * @param defaultEncoding the default encoding, if any * @return the encoding to be used for the response */ - public static String getResponseEncoding(FacesContext context, Optional defaultEncoding) { + public static String getResponseEncoding(FacesContext context, String defaultEncoding) { // 1. First get it from viewroot, if any. if (context.getViewRoot() != null) { @@ -1677,9 +1677,7 @@ public static String getResponseEncoding(FacesContext context, Optional if (encoding == null) { // 5. If still none found then fall back to specified default. - if (defaultEncoding.isPresent()) { - encoding = defaultEncoding.get(); - } + encoding = defaultEncoding; if (encoding != null && !encoding.isBlank()) { if (LOGGER.isLoggable(FINEST)) { From d52f5afb2f98d9082ca2a96a3b3f966803e34547 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 18 Jan 2025 10:18:48 -0400 Subject: [PATCH 4/5] further clarified comment --- .../sun/faces/application/view/FaceletViewHandlingStrategy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java b/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java index e64ec2198d..43cd8e1519 100644 --- a/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java +++ b/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java @@ -913,7 +913,7 @@ protected ResponseWriter createResponseWriter(FacesContext context) throws IOExc String defaultEncoding = (String) context.getAttributes().get(FACELETS_ENCODING_KEY); // Create a dummy ResponseWriter with a bogus writer, so we can figure out what - // content type and encoding the ResponseWriter is ultimately going to need. + // content type and default encoding the ResponseWriter is ultimately going to need. ResponseWriter initWriter = renderKit.createResponseWriter(NullWriter.INSTANCE, defaultContentType, defaultEncoding); String contentType = getResponseContentType(context, initWriter.getContentType()); From e57b5c77caa62aa3952d7d751777bff02d33fd2d Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 18 Jan 2025 10:29:16 -0400 Subject: [PATCH 5/5] Fix typo in IT #5543 --- .../java/org/eclipse/mojarra/test/issue5543/Issue5543IT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/issue5543/src/test/java/org/eclipse/mojarra/test/issue5543/Issue5543IT.java b/test/issue5543/src/test/java/org/eclipse/mojarra/test/issue5543/Issue5543IT.java index 8d26615051..7ac16b6292 100644 --- a/test/issue5543/src/test/java/org/eclipse/mojarra/test/issue5543/Issue5543IT.java +++ b/test/issue5543/src/test/java/org/eclipse/mojarra/test/issue5543/Issue5543IT.java @@ -57,7 +57,7 @@ void testDefaultResponseEncodingAjax() { open("issue5543.xhtml"); assertEquals("ä", input.getAttribute("value")); assertEquals("ä", output.getText()); - guardHttp(ajaxSubmit::click); + guardAjax(ajaxSubmit::click); assertEquals("ä", input.getAttribute("value")); assertEquals("ä", output.getText()); }