Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#5383 cleanup facelets encoding management #5385

Merged
merged 4 commits into from
Jan 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion impl/src/main/java/com/sun/faces/RIConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package com.sun.faces;

import java.nio.charset.StandardCharsets;

import com.sun.faces.config.manager.FacesSchema;

import jakarta.faces.render.RenderKitFactory;
Expand Down Expand Up @@ -58,7 +60,7 @@ public class RIConstants {
public static final String APPLICATION_XML_CONTENT_TYPE = "application/xml";
public static final String TEXT_XML_CONTENT_TYPE = "text/xml";
public static final String ALL_MEDIA = "*/*";
public static final String CHAR_ENCODING = "UTF-8";
public static final String CHAR_ENCODING = StandardCharsets.UTF_8.name();
public static final String FACELETS_ENCODING_KEY = "facelets.Encoding";
public static final String DEFAULT_LIFECYCLE = FACES_PREFIX + "DefaultLifecycle";
public static final String DEFAULT_STATEMANAGER = FACES_PREFIX + "DefaultStateManager";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@

package com.sun.faces.application;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.FINEST;
import static java.util.logging.Level.WARNING;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
Expand Down Expand Up @@ -80,7 +80,6 @@ private static class Utf8InfoRef {
int length;

public Utf8InfoRef(int index, int length) {
super();
this.index = index;
this.length = length;
}
Expand All @@ -102,7 +101,6 @@ private static class Utf8InfoReplacement implements Comparable<Utf8InfoReplaceme
byte[] replacement;

public Utf8InfoReplacement(Utf8InfoRef ref, String replacement) {
super();
this.ref = ref;
this.replacement = getUtf8InfoBytes(replacement);
}
Expand Down Expand Up @@ -542,15 +540,7 @@ private static String getVMClassName(Class<?> c) {
* @return the bytes for the UTF8Info constant pool entry, including the tag, length, and utf8 content.
*/
private static byte[] getUtf8InfoBytes(String text) {
byte[] utf8;
try {
utf8 = text.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// The DM_DEFAULT_ENCODING warning is acceptable here
// because we explicitly *want* to use the Java runtime's
// default encoding.
utf8 = text.getBytes();
}
byte[] utf8 = text.getBytes(UTF_8);
byte[] info = new byte[utf8.length + 3];
info[0] = 1;
info[1] = (byte) (utf8.length >> 8 & 0xff);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
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 Down Expand Up @@ -72,6 +71,7 @@
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;
Expand Down Expand Up @@ -908,22 +908,25 @@ protected ResponseWriter createResponseWriter(FacesContext context) throws IOExc
String encoding = (String) context.getAttributes().get(FACELETS_ENCODING_KEY);

// Create a dummy ResponseWriter with a bogus writer,
// so we can figure out what content type the ReponseWriter
// so we can figure out what content type and encoding the ReponseWriter
// is really going to ask for
ResponseWriter writer = renderKit.createResponseWriter(NullWriter.INSTANCE, contentType, encoding);
ResponseWriter initWriter = renderKit.createResponseWriter(NullWriter.INSTANCE, contentType, encoding);

contentType = getResponseContentType(context, writer.getContentType());
encoding = getResponseEncoding(context, writer.getCharacterEncoding());
contentType = getResponseContentType(context, initWriter.getContentType());
encoding = Util.getResponseEncoding(context, Optional.ofNullable(initWriter.getCharacterEncoding()));

// apply them to the response
char[] buffer = new char[1028];
HtmlUtils.writeTextForXML(writer, contentType, buffer);
HtmlUtils.writeTextForXML(initWriter, contentType, buffer);
String str = String.valueOf(buffer).trim();
extContext.setResponseContentType(str);
extContext.setResponseCharacterEncoding(encoding);

// Save encoding in UIViewRoot for faster consult when Util#getResponseEncoding() is invoked again elsewhere.
context.getViewRoot().getAttributes().put(FACELETS_ENCODING_KEY, encoding);

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

return writer;
}
Expand Down Expand Up @@ -974,58 +977,6 @@ protected void handleFaceletNotFound(FacesContext context, String viewId, String
context.responseComplete();
}

/**
* @param context the {@link FacesContext} for the current request
* @param orig the original encoding
* @return the encoding to be used for this response
*/
protected String getResponseEncoding(FacesContext context, String orig) {
String encoding = orig;

// 1. get it from request
encoding = context.getExternalContext().getRequestCharacterEncoding();

// 2. get it from the session
if (encoding == null) {
if (context.getExternalContext().getSession(false) != null) {
Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
encoding = (String) sessionMap.get(CHARACTER_ENCODING_KEY);
if (LOGGER.isLoggable(FINEST)) {
LOGGER.log(FINEST, "Session specified alternate encoding {0}", encoding);
}
}
}

// see if we need to override the encoding
Map<Object, Object> ctxAttributes = context.getAttributes();

// 3. check the request attribute
if (ctxAttributes.containsKey(FACELETS_ENCODING_KEY)) {
encoding = (String) ctxAttributes.get(FACELETS_ENCODING_KEY);
if (LOGGER.isLoggable(FINEST)) {
LOGGER.log(FINEST, "Facelet specified alternate encoding {0}", encoding);
}
if (null != context.getExternalContext().getSession(false)) {
Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
sessionMap.put(CHARACTER_ENCODING_KEY, encoding);
}
}

// 4. default it
if (encoding == null) {
if (null != orig && 0 < orig.length()) {
encoding = orig;
} else {
encoding = "UTF-8";
}
if (LOGGER.isLoggable(FINEST)) {
LOGGER.log(FINEST, "ResponseWriter created had a null CharacterEncoding, defaulting to {0}", orig);
}
}

return encoding;
}

/**
* @param context the {@link FacesContext} for the current request
* @param orig the original contentType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package com.sun.faces.application.view;

import static com.sun.faces.RIConstants.FACELETS_ENCODING_KEY;
import static com.sun.faces.RIConstants.SAVESTATE_FIELD_MARKER;
import static com.sun.faces.renderkit.RenderKitUtils.getResponseStateManager;
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.RENDER_KIT_ID_PARAM;
Expand All @@ -35,10 +34,8 @@
import static jakarta.servlet.http.MappingMatch.EXACT;
import static jakarta.servlet.http.MappingMatch.EXTENSION;
import static jakarta.servlet.http.MappingMatch.PATH;
import static java.text.MessageFormat.format;
import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.WARNING;

Expand Down Expand Up @@ -350,24 +347,7 @@ public boolean removeProtectedView(String urlPattern) {
*/
@Override
public String getRedirectURL(FacesContext context, String viewId, Map<String, List<String>> parameters, boolean includeViewParams) {
String encodingFromContext = (String) context.getAttributes().get(FACELETS_ENCODING_KEY);
if (encodingFromContext == null) {
encodingFromContext = (String) context.getViewRoot().getAttributes().get(FACELETS_ENCODING_KEY);
}

String responseEncoding;

if (encodingFromContext == null) {
try {
responseEncoding = context.getExternalContext().getResponseCharacterEncoding();
} catch (Exception e) {
LOGGER.log(FINE, e, () ->
format("Unable to obtain response character encoding from ExternalContext {0}. Using UTF-8.", context.getExternalContext()));
responseEncoding = "UTF-8";
}
} else {
responseEncoding = encodingFromContext;
}
String responseEncoding = Util.getResponseEncoding(context);

if (parameters != null) {
Map<String, List<String>> decodedParameters = new HashMap<>();
Expand Down
32 changes: 5 additions & 27 deletions impl/src/main/java/com/sun/faces/context/ExternalContextImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -654,9 +654,8 @@ public void redirect(String requestURI) throws IOException {
pwriter = ctx.getPartialViewContext().getPartialResponseWriter();
}
setResponseContentType(RIConstants.TEXT_XML_CONTENT_TYPE);
setResponseCharacterEncoding("UTF-8");
setResponseCharacterEncoding(RIConstants.CHAR_ENCODING);
addResponseHeader("Cache-Control", "no-cache");
// pwriter.writePreamble("<?xml version='1.0' encoding='UTF-8'?>\n");
pwriter.startDocument();
pwriter.redirect(requestURI);
pwriter.endDocument();
Expand Down Expand Up @@ -974,14 +973,7 @@ public boolean isSecure() {

@Override
public String encodeBookmarkableURL(String baseUrl, Map<String, List<String>> parameters) {
FacesContext context = FacesContext.getCurrentInstance();
String encodingFromContext = (String) context.getAttributes().get(RIConstants.FACELETS_ENCODING_KEY);
if (null == encodingFromContext) {
encodingFromContext = (String) context.getViewRoot().getAttributes().get(RIConstants.FACELETS_ENCODING_KEY);
}

String currentResponseEncoding = null != encodingFromContext ? encodingFromContext : getResponseCharacterEncoding();

String currentResponseEncoding = Util.getResponseEncoding(FacesContext.getCurrentInstance());
UrlBuilder builder = new UrlBuilder(baseUrl, currentResponseEncoding);
builder.addParameters(parameters);
return builder.createUrl();
Expand All @@ -990,14 +982,7 @@ public String encodeBookmarkableURL(String baseUrl, Map<String, List<String>> pa

@Override
public String encodeRedirectURL(String baseUrl, Map<String, List<String>> parameters) {
FacesContext context = FacesContext.getCurrentInstance();
String encodingFromContext = (String) context.getAttributes().get(RIConstants.FACELETS_ENCODING_KEY);
if (null == encodingFromContext) {
encodingFromContext = (String) context.getViewRoot().getAttributes().get(RIConstants.FACELETS_ENCODING_KEY);
}

String currentResponseEncoding = null != encodingFromContext ? encodingFromContext : getResponseCharacterEncoding();

String currentResponseEncoding = Util.getResponseEncoding(FacesContext.getCurrentInstance());
UrlBuilder builder = new UrlBuilder(baseUrl, currentResponseEncoding);
builder.addParameters(parameters);
return builder.createUrl();
Expand All @@ -1013,14 +998,7 @@ public String encodePartialActionURL(String url) {
String message = MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "url");
throw new NullPointerException(message);
}
FacesContext context = FacesContext.getCurrentInstance();
String encodingFromContext = (String) context.getAttributes().get(RIConstants.FACELETS_ENCODING_KEY);
if (null == encodingFromContext) {
encodingFromContext = (String) context.getViewRoot().getAttributes().get(RIConstants.FACELETS_ENCODING_KEY);
}

String currentResponseEncoding = null != encodingFromContext ? encodingFromContext : getResponseCharacterEncoding();

String currentResponseEncoding = Util.getResponseEncoding(FacesContext.getCurrentInstance());
UrlBuilder builder = new UrlBuilder(url, currentResponseEncoding);
return ((HttpServletResponse) response).encodeURL(builder.createUrl());
}
Expand Down Expand Up @@ -1078,7 +1056,7 @@ private void pushIfPossibleAndNecessary(String result) {

private PushBuilder getPushBuilder(FacesContext context) {
ExternalContext extContext = context.getExternalContext();

if (extContext.getRequest() instanceof HttpServletRequest) {
HttpServletRequest hreq = (HttpServletRequest) extContext.getRequest();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.PARTIAL_EXECUTE_PARAM;
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.PARTIAL_RENDER_PARAM;
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.PARTIAL_RESET_VALUES_PARAM;
import static jakarta.faces.FactoryFinder.VISIT_CONTEXT_FACTORY;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.WARNING;
import static jakarta.faces.FactoryFinder.VISIT_CONTEXT_FACTORY;

import java.io.IOException;
import java.io.Writer;
Expand Down Expand Up @@ -281,11 +281,6 @@ public void processPartial(PhaseId phaseId) {
exContext.setResponseContentType(RIConstants.TEXT_XML_CONTENT_TYPE);
exContext.addResponseHeader("Cache-Control", "no-cache");

// String encoding = writer.getCharacterEncoding( );
// if( encoding == null ) {
// encoding = "UTF-8";
// }
// writer.writePreamble("<?xml version='1.0' encoding='" + encoding + "'?>\n");
writer.startDocument();

if (isResetValues()) {
Expand Down Expand Up @@ -322,7 +317,7 @@ public void processPartial(PhaseId phaseId) {
}
}
}

private void doFlashPostPhaseActions(FacesContext ctx) {
try {
ctx.getExternalContext().getFlash().doPostPhaseActions(ctx);
Expand Down Expand Up @@ -630,7 +625,7 @@ public DelayedInitPartialResponseWriter(PartialViewContextImpl ctx) {
super(null);
this.ctx = ctx;
ExternalContext extCtx = ctx.ctx.getExternalContext();

if (extCtx.isResponseCommitted()) {
LOGGER.log(WARNING, "Response is already committed - cannot reconfigure it anymore");
}
Expand Down
5 changes: 3 additions & 2 deletions impl/src/main/java/com/sun/faces/context/UrlBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.sun.faces.context;

import static com.sun.faces.RIConstants.CHAR_ENCODING;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
Expand Down Expand Up @@ -49,7 +51,6 @@ class UrlBuilder {
public static final String PARAMETER_PAIR_SEPARATOR = "&";
public static final String PARAMETER_NAME_VALUE_SEPARATOR = "=";
public static final String FRAGMENT_SEPARATOR = "#";
public static final String DEFAULT_ENCODING = "UTF-8";

private static final List<String> NULL_LIST = Arrays.asList((String) null);

Expand All @@ -73,7 +74,7 @@ public UrlBuilder(String url, String encoding) {
}

public UrlBuilder(String url) {
this(url, DEFAULT_ENCODING);
this(url, CHAR_ENCODING);
}

// ---------------------------------------------------------- Public Methods
Expand Down
Loading
Loading