Skip to content

Commit

Permalink
Merge pull request #5385 from eclipse-ee4j/mojarra_issue_5383_cleanup…
Browse files Browse the repository at this point in the history
…_facelets_encoding_management

#5383 cleanup facelets encoding management
  • Loading branch information
BalusC authored Jan 28, 2024
2 parents 3b2a277 + 263095b commit facc1f9
Show file tree
Hide file tree
Showing 15 changed files with 138 additions and 187 deletions.
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

0 comments on commit facc1f9

Please sign in to comment.