Skip to content

Commit

Permalink
Merge pull request #5544 from eclipse-ee4j/issue_5543
Browse files Browse the repository at this point in the history
Fix Response encoding not stored in the ext context session #5543
  • Loading branch information
BalusC authored Feb 1, 2025
2 parents 5f7d9a7 + 6388bce commit 60f58f7
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -71,33 +71,12 @@
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;
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;
Expand Down Expand Up @@ -145,6 +124,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.
*/
Expand Down Expand Up @@ -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 <f:view contentType> 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 <f:view encoding> 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 default 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];
Expand All @@ -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 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);
}

// Now, clone with the real writer
ResponseWriter writer = initWriter.cloneWithWriter(extContext.getResponseOutputWriter());

Expand Down
26 changes: 12 additions & 14 deletions impl/src/main/java/com/sun/faces/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

/**
* <B>Util</B> is a class ...
*
Expand Down Expand Up @@ -1626,15 +1626,15 @@ 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);
}

/**
* @param context the {@link FacesContext} for the current request
* @param defaultEncoding the default encoding, if any
* @return the encoding to be used for the response
*/
public static String getResponseEncoding(FacesContext context, Optional<String> defaultEncoding) {
public static String getResponseEncoding(FacesContext context, String defaultEncoding) {

// 1. First get it from viewroot, if any.
if (context.getViewRoot() != null) {
Expand Down Expand Up @@ -1666,8 +1666,8 @@ public static String getResponseEncoding(FacesContext context, Optional<String>
}

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)) {
Expand All @@ -1677,9 +1677,7 @@ public static String getResponseEncoding(FacesContext context, Optional<String>

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)) {
Expand Down
41 changes: 41 additions & 0 deletions test/issue5543/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) Contributors to 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
http://www.eclipse.org/legal/epl-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: GNU General Public License,
version 2 with the GNU Classpath Exception, which is available at
https://www.gnu.org/software/classpath/license.html.
SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.eclipse.mojarra.test</groupId>
<artifactId>pom</artifactId>
<version>4.0.10-SNAPSHOT</version>
</parent>

<artifactId>issue5543</artifactId>
<packaging>war</packaging>

<name>Mojarra ${project.version} - INTEGRATION TESTS - ${project.artifactId}</name>

<dependencies>
<dependency>
<groupId>org.eclipse.mojarra.test</groupId>
<artifactId>base</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -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;
}
}
34 changes: 34 additions & 0 deletions test/issue5543/src/main/webapp/WEB-INF/web.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) Contributors to 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
http://www.eclipse.org/legal/epl-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: GNU General Public License,
version 2 with the GNU Classpath Exception, which is available at
https://www.gnu.org/software/classpath/license.html.
SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-->
<web-app
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
>
<servlet>
<servlet-name>facesServlet</servlet-name>
<servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>facesServlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
</web-app>
35 changes: 35 additions & 0 deletions test/issue5543/src/main/webapp/issue5543.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<!--
Copyright (c) Contributors to 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
http://www.eclipse.org/legal/epl-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: GNU General Public License,
version 2 with the GNU Classpath Exception, which is available at
https://www.gnu.org/software/classpath/license.html.
SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-->
<html lang="en"
xmlns:h="jakarta.faces.html"
xmlns:f="jakarta.faces.core"
xmlns:ui="jakarta.faces.facelets"
>
<h:head>
<title>issue5543</title>
</h:head>
<h:body>
<h:form id="form">
<h:inputText id="input" value="#{issue5543Bean.text}" />
<h:commandButton id="nonAjaxSubmit" value="non-ajax submit" />
<h:commandButton id="ajaxSubmit" value="ajax submit"><f:ajax execute="@form" render="@form" /></h:commandButton>
<h:outputText id="output" value="#{issue5543Bean.text}" />
</h:form>
</h:body>
</html>
Loading

0 comments on commit 60f58f7

Please sign in to comment.