diff --git a/impl/src/main/java/com/sun/faces/RIConstants.java b/impl/src/main/java/com/sun/faces/RIConstants.java
index 62b400c6b4..cffc4e0ec5 100644
--- a/impl/src/main/java/com/sun/faces/RIConstants.java
+++ b/impl/src/main/java/com/sun/faces/RIConstants.java
@@ -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;
@@ -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";
diff --git a/impl/src/main/java/com/sun/faces/application/ApplicationAssociate.java b/impl/src/main/java/com/sun/faces/application/ApplicationAssociate.java
index e08430df88..4bdabc4f1b 100644
--- a/impl/src/main/java/com/sun/faces/application/ApplicationAssociate.java
+++ b/impl/src/main/java/com/sun/faces/application/ApplicationAssociate.java
@@ -196,9 +196,12 @@ public static ApplicationAssociate getCurrentInstance() {
}
public static ApplicationAssociate getInstance() {
- FacesContext facesContext = FacesContext.getCurrentInstance();
+ return getInstance(FacesContext.getCurrentInstance());
+ }
+
+ public static ApplicationAssociate getInstance(FacesContext facesContext) {
if (facesContext == null) {
- return null;
+ return null;
}
return ApplicationAssociate.getInstance(facesContext.getExternalContext());
diff --git a/impl/src/main/java/com/sun/faces/application/ConverterPropertyEditorFactory.java b/impl/src/main/java/com/sun/faces/application/ConverterPropertyEditorFactory.java
index f5a824a2e6..c1b4c7dc21 100644
--- a/impl/src/main/java/com/sun/faces/application/ConverterPropertyEditorFactory.java
+++ b/impl/src/main/java/com/sun/faces/application/ConverterPropertyEditorFactory.java
@@ -16,6 +16,7 @@
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;
@@ -23,9 +24,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
-import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
@@ -81,7 +80,6 @@ private static class Utf8InfoRef {
int length;
public Utf8InfoRef(int index, int length) {
- super();
this.index = index;
this.length = length;
}
@@ -103,7 +101,6 @@ private static class Utf8InfoReplacement implements Comparable 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;
- utf8 = text.getBytes(StandardCharsets.UTF_8);
+ byte[] utf8 = text.getBytes(UTF_8);
byte[] info = new byte[utf8.length + 3];
info[0] = 1;
info[1] = (byte) (utf8.length >> 8 & 0xff);
diff --git a/impl/src/main/java/com/sun/faces/application/resource/FaceletWebappResourceHelper.java b/impl/src/main/java/com/sun/faces/application/resource/FaceletWebappResourceHelper.java
index 513fbb8cc7..33f708b208 100644
--- a/impl/src/main/java/com/sun/faces/application/resource/FaceletWebappResourceHelper.java
+++ b/impl/src/main/java/com/sun/faces/application/resource/FaceletWebappResourceHelper.java
@@ -19,7 +19,6 @@
import static com.sun.faces.RIConstants.FLOW_IN_JAR_PREFIX;
import static com.sun.faces.config.WebConfiguration.META_INF_CONTRACTS_DIR;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.FaceletsSuffix;
-import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.WebAppContractsDirectory;
import static jakarta.faces.application.ResourceVisitOption.TOP_LEVEL_VIEWS_ONLY;
import static java.util.Spliterator.DISTINCT;
import static java.util.Spliterators.spliteratorUnknownSize;
@@ -32,13 +31,16 @@
import java.util.Enumeration;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.stream.Stream;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.util.Util;
+import jakarta.enterprise.inject.Any;
import jakarta.faces.FacesException;
+import jakarta.faces.annotation.View;
import jakarta.faces.application.ResourceVisitOption;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
@@ -48,12 +50,12 @@ public class FaceletWebappResourceHelper extends ResourceHelper {
private static final String[] RESTRICTED_DIRECTORIES = { "/WEB-INF/", "/META-INF/" };
- private final String webAppContractsDirectory;
+ private final ResourceHelper webappResourceHelper;
private final String[] configuredExtensions;
- public FaceletWebappResourceHelper() {
+ public FaceletWebappResourceHelper(WebappResourceHelper webappResourceHelper) {
+ this.webappResourceHelper = webappResourceHelper;
WebConfiguration webConfig = WebConfiguration.getInstance();
- webAppContractsDirectory = webConfig.getOptionValue(WebAppContractsDirectory);
configuredExtensions = webConfig.getOptionValue(FaceletsSuffix, " ");
}
@@ -114,9 +116,16 @@ public ResourceInfo findResource(LibraryInfo library, String resourceName, Strin
}
public Stream getViewResources(FacesContext facesContext, String path, int maxDepth, ResourceVisitOption... options) {
- return stream(spliteratorUnknownSize(
+ Stream physicalViewResources = stream(spliteratorUnknownSize(
new ResourcePathsIterator(path, maxDepth, configuredExtensions, getRestrictedDirectories(options), facesContext.getExternalContext()),
DISTINCT), false);
+ Stream programmaticViewResources = Util.getCdiBeanManager(facesContext)
+ .getBeans(Object.class, Any.Literal.INSTANCE).stream()
+ .map(bean -> bean.getBeanClass().getAnnotation(View.class))
+ .filter(Objects::nonNull)
+ .map(View::value);
+
+ return Stream.concat(physicalViewResources, programmaticViewResources);
}
private static String[] getRestrictedDirectories(final ResourceVisitOption... options) {
@@ -150,9 +159,9 @@ private URL findResourceInfoConsideringContracts(FacesContext ctx, String baseRe
for (String contract : contracts) {
if (baseResourceName.startsWith("/")) {
- resourceName = webAppContractsDirectory + "/" + contract + baseResourceName;
+ resourceName = getBaseContractsPath() + "/" + contract + baseResourceName;
} else {
- resourceName = webAppContractsDirectory + "/" + contract + "/" + baseResourceName;
+ resourceName = getBaseContractsPath() + "/" + contract + "/" + baseResourceName;
}
url = Resource.getResourceUrl(ctx, resourceName);
@@ -226,7 +235,7 @@ public String getBaseResourcePath() {
@Override
public String getBaseContractsPath() {
- return webAppContractsDirectory;
+ return webappResourceHelper.getBaseContractsPath();
}
@Override
diff --git a/impl/src/main/java/com/sun/faces/application/resource/ResourceManager.java b/impl/src/main/java/com/sun/faces/application/resource/ResourceManager.java
index f6ac1887dd..32e1c01ceb 100644
--- a/impl/src/main/java/com/sun/faces/application/resource/ResourceManager.java
+++ b/impl/src/main/java/com/sun/faces/application/resource/ResourceManager.java
@@ -16,6 +16,8 @@
package com.sun.faces.application.resource;
+import static com.sun.faces.util.Util.ensureLeadingSlash;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -57,22 +59,25 @@ public class ResourceManager {
*/
private static final Pattern CONFIG_MIMETYPE_PATTERN = Pattern.compile("[a-z-]*/[a-z0-9.\\*-]*");
- private final FaceletWebappResourceHelper faceletWebappResourceHelper = new FaceletWebappResourceHelper();
-
/**
* {@link ResourceHelper} used for looking up webapp-based resources.
*/
- private final ResourceHelper webappResourceHelper = new WebappResourceHelper();
+ private WebappResourceHelper webappResourceHelper = new WebappResourceHelper();
+
+ /**
+ * {@link ResourceHelper} used for looking up webapp-based facelets resources.
+ */
+ private FaceletWebappResourceHelper faceletWebappResourceHelper = new FaceletWebappResourceHelper(webappResourceHelper);
/**
* {@link ResourceHelper} used for looking up classpath-based resources.
*/
- private final ClasspathResourceHelper classpathResourceHelper = new ClasspathResourceHelper();
+ private ClasspathResourceHelper classpathResourceHelper = new ClasspathResourceHelper();
/**
* Cache for storing {@link ResourceInfo} instances to reduce the cost of the resource lookups.
*/
- private final ResourceCache cache;
+ private ResourceCache cache;
/**
* Patterns used to find {@link ResourceInfo} instances that may have their content compressed.
@@ -176,6 +181,14 @@ public Stream getViewResources(FacesContext facesContext, String path, i
return faceletWebappResourceHelper.getViewResources(facesContext, path, maxDepth, options);
}
+ public String getBaseContractsPath() {
+ return faceletWebappResourceHelper.getBaseContractsPath();
+ }
+
+ public boolean isContractsResource(String path) {
+ return ensureLeadingSlash(path).startsWith(getBaseContractsPath());
+ }
+
// ----------------------------------------------------- Private Methods
private ResourceInfo findResourceCompressed(String libraryName, String resourceName, boolean isViewResource, String localePrefix, List contracts,
diff --git a/impl/src/main/java/com/sun/faces/application/view/FaceletFullStateManagementStrategy.java b/impl/src/main/java/com/sun/faces/application/view/FaceletFullStateManagementStrategy.java
index 22cbc0b0fd..672b2c3009 100644
--- a/impl/src/main/java/com/sun/faces/application/view/FaceletFullStateManagementStrategy.java
+++ b/impl/src/main/java/com/sun/faces/application/view/FaceletFullStateManagementStrategy.java
@@ -82,7 +82,7 @@ public class FaceletFullStateManagementStrategy extends StateManagementStrategy
/**
* Stores the skip hint.
*/
- private static final String SKIP_ITERATION_HINT = "jakarta.faces.visit.SKIP_ITERATION";
+ private static final Set SKIP_ITERATION_HINT = EnumSet.of(SKIP_ITERATION);
/**
* Stores the class map.
@@ -185,41 +185,34 @@ private UIComponent locateComponentByClientId(final FacesContext context, final
final List found = new ArrayList<>();
UIComponent result = null;
- try {
- context.getAttributes().put(SKIP_ITERATION_HINT, true);
- Set hints = EnumSet.of(VisitHint.SKIP_ITERATION);
-
- VisitContext visitContext = VisitContext.createVisitContext(context, null, hints);
- subTree.visitTree(visitContext, (visitContext1, component) -> {
- VisitResult result1 = ACCEPT;
- if (component.getClientId(visitContext1.getFacesContext()).equals(clientId)) {
- /*
- * If the client id matches up we have found our match.
- */
- found.add(component);
- result1 = COMPLETE;
- } else if (component instanceof UIForm) {
- /*
- * If the component is a UIForm and it is prepending its id then we can short circuit out of here if the the client id
- * of the component we are trying to find does not begin with the id of the UIForm.
- */
- UIForm form = (UIForm) component;
- if (form.isPrependId() && !clientId.startsWith(form.getClientId(visitContext1.getFacesContext()))) {
- result1 = REJECT;
- }
- } else if (component instanceof NamingContainer && !clientId.startsWith(component.getClientId(visitContext1.getFacesContext()))) {
- /*
- * If the component is a naming container then assume it is prepending its id so if our client id we are looking for
- * does not start with the naming container id we can skip visiting this tree.
- */
+ VisitContext visitContext = VisitContext.createVisitContext(context, null, SKIP_ITERATION_HINT);
+ subTree.visitTree(visitContext, (visitContext1, component) -> {
+ VisitResult result1 = ACCEPT;
+ if (component.getClientId(visitContext1.getFacesContext()).equals(clientId)) {
+ /*
+ * If the client id matches up we have found our match.
+ */
+ found.add(component);
+ result1 = COMPLETE;
+ } else if (component instanceof UIForm) {
+ /*
+ * If the component is a UIForm and it is prepending its id then we can short circuit out of here if the the client id
+ * of the component we are trying to find does not begin with the id of the UIForm.
+ */
+ UIForm form = (UIForm) component;
+ if (form.isPrependId() && !clientId.startsWith(form.getClientId(visitContext1.getFacesContext()))) {
result1 = REJECT;
}
+ } else if (component instanceof NamingContainer && !clientId.startsWith(component.getClientId(visitContext1.getFacesContext()))) {
+ /*
+ * If the component is a naming container then assume it is prepending its id so if our client id we are looking for
+ * does not start with the naming container id we can skip visiting this tree.
+ */
+ result1 = REJECT;
+ }
- return result1;
- });
- } finally {
- context.getAttributes().remove(SKIP_ITERATION_HINT);
- }
+ return result1;
+ });
if (!found.isEmpty()) {
result = found.get(0);
@@ -311,36 +304,30 @@ private void restoreComponentState(final FacesContext context, final HashMap hints = EnumSet.of(SKIP_ITERATION);
- VisitContext visitContext = VisitContext.createVisitContext(context, null, hints);
+ VisitContext visitContext = VisitContext.createVisitContext(context, null, SKIP_ITERATION_HINT);
- viewRoot.visitTree(visitContext, (visitContext1, component) -> {
- VisitResult result = ACCEPT;
+ viewRoot.visitTree(visitContext, (visitContext1, component) -> {
+ VisitResult result = ACCEPT;
- String clientId = component.getClientId(context);
- Object stateObj = state.get(clientId);
+ String clientId = component.getClientId(context);
+ Object stateObj = state.get(clientId);
- if (stateObj != null && !stateContext.componentAddedDynamically(component)) {
- boolean restoreStateNow = true;
- if (stateObj instanceof StateHolderSaver) {
- restoreStateNow = !((StateHolderSaver) stateObj).componentAddedDynamically();
- }
- if (restoreStateNow) {
- try {
- component.restoreState(context, stateObj);
- } catch (Exception e) {
- throw new FacesException(e);
- }
+ if (stateObj != null && !stateContext.componentAddedDynamically(component)) {
+ boolean restoreStateNow = true;
+ if (stateObj instanceof StateHolderSaver) {
+ restoreStateNow = !((StateHolderSaver) stateObj).componentAddedDynamically();
+ }
+ if (restoreStateNow) {
+ try {
+ component.restoreState(context, stateObj);
+ } catch (Exception e) {
+ throw new FacesException(e);
}
}
+ }
- return result;
- });
- } finally {
- context.getAttributes().remove(SKIP_ITERATION_HINT);
- }
+ return result;
+ });
}
/**
@@ -592,33 +579,27 @@ private Object saveComponentState(FacesContext context) {
final UIViewRoot viewRoot = context.getViewRoot();
final FacesContext finalContext = context;
- context.getAttributes().put(SKIP_ITERATION_HINT, true);
- Set hints = EnumSet.of(SKIP_ITERATION);
- VisitContext visitContext = VisitContext.createVisitContext(context, null, hints);
+ VisitContext visitContext = VisitContext.createVisitContext(context, null, SKIP_ITERATION_HINT);
- try {
- viewRoot.visitTree(visitContext, (context1, component) -> {
- VisitResult result = ACCEPT;
- Object stateObj;
- if (!component.isTransient()) {
- if (stateContext.componentAddedDynamically(component)) {
- component.getAttributes().put(DYNAMIC_COMPONENT, new Integer(getProperChildIndex(component)));
- stateObj = new StateHolderSaver(finalContext, component);
- } else {
- stateObj = component.saveState(finalContext);
- }
- if (stateObj != null) {
- stateMap.put(component.getClientId(finalContext), stateObj);
- }
+ viewRoot.visitTree(visitContext, (context1, component) -> {
+ VisitResult result = ACCEPT;
+ Object stateObj;
+ if (!component.isTransient()) {
+ if (stateContext.componentAddedDynamically(component)) {
+ component.getAttributes().put(DYNAMIC_COMPONENT, new Integer(getProperChildIndex(component)));
+ stateObj = new StateHolderSaver(finalContext, component);
} else {
- result = REJECT;
+ stateObj = component.saveState(finalContext);
}
+ if (stateObj != null) {
+ stateMap.put(component.getClientId(finalContext), stateObj);
+ }
+ } else {
+ result = REJECT;
+ }
- return result;
- });
- } finally {
- context.getAttributes().remove(SKIP_ITERATION_HINT);
- }
+ return result;
+ });
return stateMap;
}
diff --git a/impl/src/main/java/com/sun/faces/application/view/FaceletPartialStateManagementStrategy.java b/impl/src/main/java/com/sun/faces/application/view/FaceletPartialStateManagementStrategy.java
index 29e763978f..6faf35945e 100644
--- a/impl/src/main/java/com/sun/faces/application/view/FaceletPartialStateManagementStrategy.java
+++ b/impl/src/main/java/com/sun/faces/application/view/FaceletPartialStateManagementStrategy.java
@@ -68,10 +68,16 @@ public class FaceletPartialStateManagementStrategy extends StateManagementStrate
* Stores the logger.
*/
private static final Logger LOGGER = FacesLogger.APPLICATION_VIEW.getLogger();
+
/**
* Stores the skip hint.
*/
- private static final String SKIP_ITERATION_HINT = "jakarta.faces.visit.SKIP_ITERATION";
+ private static final Set SKIP_ITERATION_HINT = EnumSet.of(SKIP_ITERATION);
+
+ /**
+ * Stores the skip and lifecycle hints.
+ */
+ private static final Set SKIP_ITERATION_AND_EXECUTE_LIFECYCLE_HINTS = EnumSet.of(VisitHint.SKIP_ITERATION, VisitHint.EXECUTE_LIFECYCLE);
/**
* Constructor.
@@ -102,41 +108,34 @@ private UIComponent locateComponentByClientId(final FacesContext context, final
final List found = new ArrayList<>();
UIComponent result = null;
- try {
- context.getAttributes().put(SKIP_ITERATION_HINT, true);
- Set hints = EnumSet.of(SKIP_ITERATION);
-
- VisitContext visitContext = VisitContext.createVisitContext(context, null, hints);
- subTree.visitTree(visitContext, (visitContext1, component) -> {
- VisitResult result1 = ACCEPT;
- if (component.getClientId(visitContext1.getFacesContext()).equals(clientId)) {
- /*
- * If the client id matches up we have found our match.
- */
- found.add(component);
- result1 = COMPLETE;
- } else if (component instanceof UIForm) {
- /*
- * If the component is a UIForm and it is prepending its id then we can short circuit out of here if the the client id
- * of the component we are trying to find does not begin with the id of the UIForm.
- */
- UIForm form = (UIForm) component;
- if (form.isPrependId() && !clientId.startsWith(form.getClientId(visitContext1.getFacesContext()))) {
- result1 = REJECT;
- }
- } else if (component instanceof NamingContainer && !clientId.startsWith(component.getClientId(visitContext1.getFacesContext()))) {
- /*
- * If the component is a naming container then assume it is prepending its id so if our client id we are looking for
- * does not start with the naming container id we can skip visiting this tree.
- */
+ VisitContext visitContext = VisitContext.createVisitContext(context, null, SKIP_ITERATION_HINT);
+ subTree.visitTree(visitContext, (visitContext1, component) -> {
+ VisitResult result1 = ACCEPT;
+ if (component.getClientId(visitContext1.getFacesContext()).equals(clientId)) {
+ /*
+ * If the client id matches up we have found our match.
+ */
+ found.add(component);
+ result1 = COMPLETE;
+ } else if (component instanceof UIForm) {
+ /*
+ * If the component is a UIForm and it is prepending its id then we can short circuit out of here if the the client id
+ * of the component we are trying to find does not begin with the id of the UIForm.
+ */
+ UIForm form = (UIForm) component;
+ if (form.isPrependId() && !clientId.startsWith(form.getClientId(visitContext1.getFacesContext()))) {
result1 = REJECT;
}
+ } else if (component instanceof NamingContainer && !clientId.startsWith(component.getClientId(visitContext1.getFacesContext()))) {
+ /*
+ * If the component is a naming container then assume it is prepending its id so if our client id we are looking for
+ * does not start with the naming container id we can skip visiting this tree.
+ */
+ result1 = REJECT;
+ }
- return result1;
- });
- } finally {
- context.getAttributes().remove(SKIP_ITERATION_HINT);
- }
+ return result1;
+ });
if (!found.isEmpty()) {
result = found.get(0);
@@ -343,9 +342,7 @@ public UIViewRoot restoreView(FacesContext context, String viewId, String render
try {
stateContext.setTrackViewModifications(false);
- context.getAttributes().put(SKIP_ITERATION_HINT, true);
- Set hints = EnumSet.of(VisitHint.SKIP_ITERATION, VisitHint.EXECUTE_LIFECYCLE);
- VisitContext visitContext = VisitContext.createVisitContext(context, null, hints);
+ VisitContext visitContext = VisitContext.createVisitContext(context, null, SKIP_ITERATION_AND_EXECUTE_LIFECYCLE_HINTS);
viewRoot.visitTree(visitContext, (context1, target) -> {
VisitResult result = VisitResult.ACCEPT;
String cid = target.getClientId(context1.getFacesContext());
@@ -370,7 +367,6 @@ public UIViewRoot restoreView(FacesContext context, String viewId, String render
restoreDynamicActions(context, stateContext, state);
} finally {
stateContext.setTrackViewModifications(true);
- context.getAttributes().remove(SKIP_ITERATION_HINT);
}
} else {
viewRoot = null;
@@ -436,33 +432,27 @@ public Object saveView(FacesContext context) {
final Map stateMap = new HashMap<>();
final StateContext stateContext = StateContext.getStateContext(context);
- context.getAttributes().put(SKIP_ITERATION_HINT, true);
- Set hints = EnumSet.of(VisitHint.SKIP_ITERATION);
- VisitContext visitContext = VisitContext.createVisitContext(context, null, hints);
+ VisitContext visitContext = VisitContext.createVisitContext(context, null, SKIP_ITERATION_HINT);
final FacesContext finalContext = context;
- try {
- viewRoot.visitTree(visitContext, (context1, target) -> {
- VisitResult result = VisitResult.ACCEPT;
- Object stateObj;
- if (!target.isTransient()) {
- if (stateContext.componentAddedDynamically(target)) {
- target.getAttributes().put(DYNAMIC_COMPONENT, target.getParent().getChildren().indexOf(target));
- stateObj = new StateHolderSaver(finalContext, target);
- } else {
- stateObj = target.saveState(context1.getFacesContext());
- }
- if (stateObj != null) {
- stateMap.put(target.getClientId(context1.getFacesContext()), stateObj);
- }
+ viewRoot.visitTree(visitContext, (context1, target) -> {
+ VisitResult result = VisitResult.ACCEPT;
+ Object stateObj;
+ if (!target.isTransient()) {
+ if (stateContext.componentAddedDynamically(target)) {
+ target.getAttributes().put(DYNAMIC_COMPONENT, target.getParent().getChildren().indexOf(target));
+ stateObj = new StateHolderSaver(finalContext, target);
} else {
- return VisitResult.REJECT;
+ stateObj = target.saveState(context1.getFacesContext());
}
- return result;
- });
- } finally {
- context.getAttributes().remove(SKIP_ITERATION_HINT);
- }
+ if (stateObj != null) {
+ stateMap.put(target.getClientId(context1.getFacesContext()), stateObj);
+ }
+ } else {
+ return VisitResult.REJECT;
+ }
+ return result;
+ });
saveDynamicActions(context, stateContext, stateMap);
StateContext.release(context);
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 527397871c..bd4e28529a 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,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;
@@ -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;
@@ -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;
}
@@ -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 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
- *
+ *
* @param context the involved faces context
* @param outgoingResponseIsRedirect whether outgoing response is redirect
*/
@@ -1313,15 +1313,7 @@ void expireNext_MovePreviousToNext() {
void decode(FacesContext context, ELFlash flash, Cookie cookie) throws InvalidKeyException {
String temp;
String value;
-
- String urlDecodedValue = null;
-
- try {
- urlDecodedValue = URLDecoder.decode(cookie.getValue(), "UTF-8");
- } catch (UnsupportedEncodingException uee) {
- urlDecodedValue = cookie.getValue();
- }
-
+ String urlDecodedValue = URLDecoder.decode(cookie.getValue(), UTF_8);
value = guard.decrypt(urlDecodedValue);
try {
@@ -1390,16 +1382,10 @@ void decode(FacesContext context, ELFlash flash, Cookie cookie) throws InvalidKe
*
*/
Cookie encode() {
- Cookie result = null;
-
String value = (null != previousRequestFlashInfo ? previousRequestFlashInfo.encode() : "") + "_"
+ (null != nextRequestFlashInfo ? nextRequestFlashInfo.encode() : "");
String encryptedValue = guard.encrypt(value);
- try {
- result = new Cookie(FLASH_COOKIE_NAME, URLEncoder.encode(encryptedValue, "UTF-8"));
- } catch (UnsupportedEncodingException uee) {
- result = new Cookie(FLASH_COOKIE_NAME, encryptedValue);
- }
+ Cookie result = new Cookie(FLASH_COOKIE_NAME, URLEncoder.encode(encryptedValue, UTF_8));
if (1 == value.length()) {
result.setMaxAge(0);
diff --git a/impl/src/main/java/com/sun/faces/facelets/component/UIRepeat.java b/impl/src/main/java/com/sun/faces/facelets/component/UIRepeat.java
index 217d790f7a..99f5456749 100644
--- a/impl/src/main/java/com/sun/faces/facelets/component/UIRepeat.java
+++ b/impl/src/main/java/com/sun/faces/facelets/component/UIRepeat.java
@@ -938,13 +938,16 @@ public void broadcast(FacesEvent event) throws AbortProcessingException {
FacesEvent target = idxEvent.getTarget();
FacesContext ctx = target.getFacesContext();
resetDataModel(ctx);
- int prevIndex = index;
+ int idx = idxEvent.getIndex();
+ int prevIndex = this.index;
+ boolean needsToSetIndex = idx != -1 || prevIndex != -1; // #5213
UIComponent source = target.getComponent();
UIComponent compositeParent = null;
try {
int rowCount = getDataModel().getRowCount();
- int idx = idxEvent.getIndex();
- setIndex(ctx, idx);
+ if (needsToSetIndex) {
+ setIndex(ctx, idx);
+ }
Integer begin = getBegin();
Integer end = getEnd();
Integer step = getStep();
@@ -969,7 +972,9 @@ public void broadcast(FacesEvent event) throws AbortProcessingException {
compositeParent.popComponentFromEL(ctx);
}
updateIterationStatus(ctx, null);
- setIndex(ctx, prevIndex);
+ if (needsToSetIndex) {
+ setIndex(ctx, prevIndex);
+ }
}
} else {
super.broadcast(event);
diff --git a/impl/src/main/java/com/sun/faces/facelets/impl/DefaultFacelet.java b/impl/src/main/java/com/sun/faces/facelets/impl/DefaultFacelet.java
index 83ae43e195..824a8c42a5 100644
--- a/impl/src/main/java/com/sun/faces/facelets/impl/DefaultFacelet.java
+++ b/impl/src/main/java/com/sun/faces/facelets/impl/DefaultFacelet.java
@@ -230,14 +230,14 @@ public long getCreateTime() {
}
/**
- * Delegates resolution to DefaultFaceletFactory reference. Also, caches URLs for relative paths.
+ * Delegates resolution to DefaultFaceletFactory reference. Also, caches URLs for absolute paths.
*
- * @param path a relative url path
+ * @param relativePath a relative url path
* @return URL pointing to destination
* @throws IOException if there is a problem creating the URL for the path specified
*/
- private URL getRelativePath(String path) throws IOException {
- return factory.resolveURL(src, path);
+ private URL resolveURL(String relativePath) throws IOException {
+ return factory.resolveURL(src, relativePath);
}
/**
@@ -268,21 +268,21 @@ private void include(DefaultFaceletContext ctx, UIComponent parent) throws IOExc
/**
* Used for delegation by the DefaultFaceletContext. First pulls the URL from {@link #getRelativePath(String)
- * getRelativePath(String)}, then calls
+ * getRelativePath(String)}, then validates that the path does not represent a contracts resource, then calls
* {@link #include(DefaultFaceletContext, jakarta.faces.component.UIComponent, String)}.
*
* @see FaceletContext#includeFacelet(UIComponent, String)
* @param ctx FaceletContext to pass to the included Facelet
* @param parent UIComponent to apply changes to
- * @param path relative path to the desired Facelet from the FaceletContext
+ * @param relativePath relative path to the desired Facelet from the FaceletContext
* @throws IOException
* @throws FacesException
* @throws FaceletException
* @throws ELException
*/
- public void include(DefaultFaceletContext ctx, UIComponent parent, String path) throws IOException {
+ public void include(DefaultFaceletContext ctx, UIComponent parent, String relativePath) throws IOException {
URL url;
- if (path.equals(JAKARTA_FACES_ERROR_XHTML)) {
+ if (relativePath.equals(JAKARTA_FACES_ERROR_XHTML)) {
if (isDevelopment(ctx)) {
// try using this class' ClassLoader
url = getErrorFacelet(DefaultFacelet.class.getClassLoader());
@@ -293,7 +293,10 @@ public void include(DefaultFaceletContext ctx, UIComponent parent, String path)
return;
}
} else {
- url = getRelativePath(path);
+ url = resolveURL(relativePath);
+ if (factory.isContractsResource(url)) {
+ throw new IOException("Contract resources cannot be accessed this way");
+ }
}
this.include(ctx, parent, url);
}
diff --git a/impl/src/main/java/com/sun/faces/facelets/impl/DefaultFaceletFactory.java b/impl/src/main/java/com/sun/faces/facelets/impl/DefaultFaceletFactory.java
index b0678ebcb4..082208d22c 100644
--- a/impl/src/main/java/com/sun/faces/facelets/impl/DefaultFaceletFactory.java
+++ b/impl/src/main/java/com/sun/faces/facelets/impl/DefaultFaceletFactory.java
@@ -44,6 +44,8 @@
import java.util.regex.Pattern;
import com.sun.faces.RIConstants;
+import com.sun.faces.application.ApplicationAssociate;
+import com.sun.faces.application.resource.ResourceManager;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.context.FacesFileNotFoundException;
import com.sun.faces.facelets.compiler.Compiler;
@@ -81,7 +83,9 @@ public class DefaultFaceletFactory {
// provides a custom one. The DefaultResourceResolver simply uses
// the ResourceHandler to do its work.
private DefaultResourceResolver resolver;
+ private ResourceManager manager;
private URL baseUrl;
+ private String baseUrlAsString;
private long refreshPeriod;
private FaceletCache cache;
private ConcurrentMap> cachePerContract;
@@ -104,7 +108,9 @@ public final void init(FacesContext facesContext, Compiler compiler, DefaultReso
this.compiler = compiler;
cachePerContract = new ConcurrentHashMap<>();
this.resolver = resolver;
+ this.manager = ApplicationAssociate.getInstance(externalContext).getResourceManager();
baseUrl = resolver.resolveUrl("/");
+ baseUrlAsString = baseUrl.toExternalForm();
this.idMappers = config.isOptionEnabled(UseFaceletsID) ? null : new Cache<>(new IdMapperFactory());
refreshPeriod = refreshPeriod >= 0 ? refreshPeriod * 1000 : -1;
this.refreshPeriod = refreshPeriod;
@@ -167,6 +173,22 @@ public URL resolveURL(URL source, String path) throws IOException {
return new URL(source, path);
}
+ /**
+ * Returns true if given url is a contracts resource.
+ * @param url source url
+ * @return true if given url is a contracts resource.
+ */
+ public boolean isContractsResource(URL url) {
+ String urlAsString = url.toExternalForm();
+
+ if (!urlAsString.startsWith(baseUrlAsString)) {
+ return false;
+ }
+
+ String path = urlAsString.substring(baseUrlAsString.length());
+ return manager.isContractsResource(path);
+ }
+
/**
* Create a Facelet from the passed URL. This method checks if the cached Facelet needs to be refreshed before
* returning. If so, uses the passed URL to build a new instance;
diff --git a/impl/src/main/java/com/sun/faces/facelets/tag/composite/InsertChildrenHandler.java b/impl/src/main/java/com/sun/faces/facelets/tag/composite/InsertChildrenHandler.java
index 9386ffb60f..1c66d2b92e 100644
--- a/impl/src/main/java/com/sun/faces/facelets/tag/composite/InsertChildrenHandler.java
+++ b/impl/src/main/java/com/sun/faces/facelets/tag/composite/InsertChildrenHandler.java
@@ -89,7 +89,7 @@ private class RelocateChildrenListener extends RelocateListener {
this.ctx = ctx;
this.component = component;
if (!component.getAttributes().containsKey(INDEX_ATTRIBUTE)) {
- component.getAttributes().put(INDEX_ATTRIBUTE, idx); // NOTE: this is also used by AjaxBehaviorRenderer in order to detect if f:ajax was handled by cc:insertChildren. See also #5032
+ component.getAttributes().put(INDEX_ATTRIBUTE, idx);
}
this.idx = idx;
this.location = location;
diff --git a/impl/src/main/java/com/sun/faces/facelets/tag/composite/RetargetedAjaxBehavior.java b/impl/src/main/java/com/sun/faces/facelets/tag/composite/RetargetedAjaxBehavior.java
new file mode 100644
index 0000000000..fd5a4b53db
--- /dev/null
+++ b/impl/src/main/java/com/sun/faces/facelets/tag/composite/RetargetedAjaxBehavior.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * 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
+ */
+
+package com.sun.faces.facelets.tag.composite;
+
+import java.util.Collection;
+import java.util.Set;
+
+import jakarta.el.ValueExpression;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.behavior.AjaxBehavior;
+import jakarta.faces.component.behavior.ClientBehaviorContext;
+import jakarta.faces.component.behavior.ClientBehaviorHint;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.event.AbortProcessingException;
+import jakarta.faces.event.AjaxBehaviorListener;
+import jakarta.faces.event.BehaviorEvent;
+import jakarta.faces.render.ClientBehaviorRenderer;
+
+/**
+ * Basically represents {@code } which is retargeted by {@code } in {@code AjaxHandler} and checked in {@code AjaxBehaviorRenderer}.
+ *
+ * We should probably introduce {@code AjaxBehaviorWrapper} in Faces.next to reduce boilerplate like this.
+ *
+ * - https://github.com/jakartaee/faces/issues/1567
+ * - https://github.com/eclipse-ee4j/mojarra/issues/5032
+ */
+public class RetargetedAjaxBehavior extends AjaxBehavior {
+
+ private AjaxBehavior retargeted;
+
+ public RetargetedAjaxBehavior(AjaxBehavior retargeted) {
+ this.retargeted = retargeted;
+ }
+
+ @Override
+ public String getScript(ClientBehaviorContext behaviorContext) {
+ ClientBehaviorRenderer renderer = getRenderer(behaviorContext.getFacesContext());
+ return renderer != null ? renderer.getScript(behaviorContext, this) : null;
+ }
+
+ @Override
+ public void broadcast(BehaviorEvent event) throws AbortProcessingException {
+ retargeted.broadcast(event);
+ }
+
+ @Override
+ public int hashCode() {
+ return retargeted.hashCode();
+ }
+
+ @Override
+ public String getRendererType() {
+ return retargeted.getRendererType();
+ }
+
+ @Override
+ public void decode(FacesContext context, UIComponent component) {
+ retargeted.decode(context, component);
+ }
+
+ @Override
+ public boolean isTransient() {
+ return retargeted.isTransient();
+ }
+
+ @Override
+ public Set getHints() {
+ return retargeted.getHints();
+ }
+
+ @Override
+ public void setTransient(boolean transientFlag) {
+ retargeted.setTransient(transientFlag);
+ }
+
+ @Override
+ public String getOnerror() {
+ return retargeted.getOnerror();
+ }
+
+ @Override
+ public void setOnerror(String onerror) {
+ retargeted.setOnerror(onerror);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return retargeted.equals(obj);
+ }
+
+ @Override
+ public String getOnevent() {
+ return retargeted.getOnevent();
+ }
+
+ @Override
+ public void setOnevent(String onevent) {
+ retargeted.setOnevent(onevent);
+ }
+
+ @Override
+ public void markInitialState() {
+ retargeted.markInitialState();
+ }
+
+ @Override
+ public boolean initialStateMarked() {
+ return retargeted.initialStateMarked();
+ }
+
+ @Override
+ public Collection getExecute() {
+ return retargeted.getExecute();
+ }
+
+ @Override
+ public void clearInitialState() {
+ retargeted.clearInitialState();
+ }
+
+ @Override
+ public void setExecute(Collection execute) {
+ retargeted.setExecute(execute);
+ }
+
+ @Override
+ public String getDelay() {
+ return retargeted.getDelay();
+ }
+
+ @Override
+ public void setDelay(String delay) {
+ retargeted.setDelay(delay);
+ }
+
+ @Override
+ public Collection getRender() {
+ return retargeted.getRender();
+ }
+
+ @Override
+ public void setRender(Collection render) {
+ retargeted.setRender(render);
+ }
+
+ @Override
+ public boolean isResetValues() {
+ return retargeted.isResetValues();
+ }
+
+ @Override
+ public void setResetValues(boolean resetValues) {
+ retargeted.setResetValues(resetValues);
+ }
+
+ @Override
+ public boolean isDisabled() {
+ return retargeted.isDisabled();
+ }
+
+ @Override
+ public void setDisabled(boolean disabled) {
+ retargeted.setDisabled(disabled);
+ }
+
+ @Override
+ public boolean isImmediate() {
+ return retargeted.isImmediate();
+ }
+
+ @Override
+ public void setImmediate(boolean immediate) {
+ retargeted.setImmediate(immediate);
+ }
+
+ @Override
+ public boolean isImmediateSet() {
+ return retargeted.isImmediateSet();
+ }
+
+ @Override
+ public String toString() {
+ return retargeted.toString();
+ }
+
+ @Override
+ public boolean isResetValuesSet() {
+ return retargeted.isResetValuesSet();
+ }
+
+ @Override
+ public ValueExpression getValueExpression(String name) {
+ return retargeted.getValueExpression(name);
+ }
+
+ @Override
+ public void setValueExpression(String name, ValueExpression binding) {
+ retargeted.setValueExpression(name, binding);
+ }
+
+ @Override
+ public void addAjaxBehaviorListener(AjaxBehaviorListener listener) {
+ retargeted.addAjaxBehaviorListener(listener);
+ }
+
+ @Override
+ public void removeAjaxBehaviorListener(AjaxBehaviorListener listener) {
+ retargeted.removeAjaxBehaviorListener(listener);
+ }
+
+ @Override
+ public Object saveState(FacesContext context) {
+ return retargeted.saveState(context);
+ }
+
+ @Override
+ public void restoreState(FacesContext context, Object state) {
+ retargeted.restoreState(context, state);
+ }
+}
diff --git a/impl/src/main/java/com/sun/faces/facelets/tag/faces/ComponentTagHandlerDelegateImpl.java b/impl/src/main/java/com/sun/faces/facelets/tag/faces/ComponentTagHandlerDelegateImpl.java
index 4a9b9c1637..3a58883cce 100644
--- a/impl/src/main/java/com/sun/faces/facelets/tag/faces/ComponentTagHandlerDelegateImpl.java
+++ b/impl/src/main/java/com/sun/faces/facelets/tag/faces/ComponentTagHandlerDelegateImpl.java
@@ -462,10 +462,7 @@ protected UIComponent findChild(FaceletContext ctx, UIComponent parent, String t
protected UIComponent findReparentedComponent(FaceletContext ctx, UIComponent parent, String tagId) {
UIComponent facet = parent.getFacets().get(UIComponent.COMPOSITE_FACET_NAME);
if (facet != null) {
- UIComponent newParent = facet.findComponent((String) parent.getAttributes().get(tagId));
- if (newParent != null) {
- return ComponentSupport.findChildByTagId(ctx.getFacesContext(), newParent, tagId);
- }
+ return findChild(ctx, facet, tagId);
}
return null;
}
diff --git a/impl/src/main/java/com/sun/faces/facelets/tag/faces/core/AjaxHandler.java b/impl/src/main/java/com/sun/faces/facelets/tag/faces/core/AjaxHandler.java
index eef385f574..dda42ad98d 100644
--- a/impl/src/main/java/com/sun/faces/facelets/tag/faces/core/AjaxHandler.java
+++ b/impl/src/main/java/com/sun/faces/facelets/tag/faces/core/AjaxHandler.java
@@ -32,6 +32,7 @@
import com.sun.faces.component.behavior.AjaxBehaviors;
import com.sun.faces.facelets.tag.TagHandlerImpl;
import com.sun.faces.facelets.tag.composite.BehaviorHolderWrapper;
+import com.sun.faces.facelets.tag.composite.RetargetedAjaxBehavior;
import com.sun.faces.facelets.tag.faces.CompositeComponentTagHandler;
import com.sun.faces.renderkit.RenderKitUtils;
@@ -313,6 +314,7 @@ private AjaxBehavior createAjaxBehavior(FaceletContext ctx, UIComponent parent,
executeClientIds.remove("@this");
stream(targetClientIds.trim().split(" +")).map(id -> "@this" + separatorChar + id).forEach(executeClientIds::add);
behavior.setExecute(executeClientIds);
+ behavior = new RetargetedAjaxBehavior(behavior);
}
}
}
diff --git a/impl/src/main/java/com/sun/faces/facelets/tag/ui/CompositionHandler.java b/impl/src/main/java/com/sun/faces/facelets/tag/ui/CompositionHandler.java
index 075c1d3ae4..cf004f77a4 100644
--- a/impl/src/main/java/com/sun/faces/facelets/tag/ui/CompositionHandler.java
+++ b/impl/src/main/java/com/sun/faces/facelets/tag/ui/CompositionHandler.java
@@ -25,7 +25,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;
-import com.sun.faces.config.WebConfiguration;
import com.sun.faces.facelets.FaceletContextImplBase;
import com.sun.faces.facelets.TemplateClient;
import com.sun.faces.facelets.el.VariableMapperWrapper;
@@ -127,16 +126,12 @@ public void apply(FaceletContext ctxObj, UIComponent parent) throws IOException
if (path.trim().length() == 0) {
throw new TagAttributeException(tag, template, "Invalid path : " + path);
}
- WebConfiguration webConfig = WebConfiguration.getInstance();
- if (path.startsWith(webConfig.getOptionValue(WebConfiguration.WebContextInitParameter.WebAppContractsDirectory))) {
- throw new TagAttributeException(tag, template, "Invalid path, contract resources cannot be accessed this way : " + path);
- }
ctx.includeFacelet(parent, path);
} catch (IOException e) {
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, e.toString(), e);
}
- throw new TagAttributeException(tag, template, "Invalid path : " + path);
+ throw new TagAttributeException(tag, template, "Invalid path : " + path, e);
} finally {
ctx.popClient(this);
ctx.setVariableMapper(orig);
diff --git a/impl/src/main/java/com/sun/faces/facelets/tag/ui/DecorateHandler.java b/impl/src/main/java/com/sun/faces/facelets/tag/ui/DecorateHandler.java
index 0d500e5dd6..7af0fbf184 100644
--- a/impl/src/main/java/com/sun/faces/facelets/tag/ui/DecorateHandler.java
+++ b/impl/src/main/java/com/sun/faces/facelets/tag/ui/DecorateHandler.java
@@ -25,7 +25,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;
-import com.sun.faces.config.WebConfiguration;
import com.sun.faces.facelets.FaceletContextImplBase;
import com.sun.faces.facelets.TemplateClient;
import com.sun.faces.facelets.el.VariableMapperWrapper;
@@ -108,16 +107,12 @@ public void apply(FaceletContext ctxObj, UIComponent parent) throws IOException
if (path.trim().length() == 0) {
throw new TagAttributeException(tag, template, "Invalid path : " + path);
}
- WebConfiguration webConfig = WebConfiguration.getInstance();
- if (path.startsWith(webConfig.getOptionValue(WebConfiguration.WebContextInitParameter.WebAppContractsDirectory))) {
- throw new TagAttributeException(tag, template, "Invalid path, contract resources cannot be accessed this way : " + path);
- }
ctx.includeFacelet(parent, path);
} catch (IOException e) {
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, e.toString(), e);
}
- throw new TagAttributeException(tag, template, "Invalid path : " + path);
+ throw new TagAttributeException(tag, template, "Invalid path : " + path, e);
} finally {
ctx.setVariableMapper(orig);
ctx.popClient(this);
diff --git a/impl/src/main/java/com/sun/faces/facelets/tag/ui/IncludeHandler.java b/impl/src/main/java/com/sun/faces/facelets/tag/ui/IncludeHandler.java
index f78c6dacae..0704901d5d 100644
--- a/impl/src/main/java/com/sun/faces/facelets/tag/ui/IncludeHandler.java
+++ b/impl/src/main/java/com/sun/faces/facelets/tag/ui/IncludeHandler.java
@@ -20,7 +20,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;
-import com.sun.faces.config.WebConfiguration;
import com.sun.faces.facelets.el.VariableMapperWrapper;
import com.sun.faces.facelets.tag.TagHandlerImpl;
import com.sun.faces.util.FacesLogger;
@@ -76,16 +75,12 @@ public void apply(FaceletContext ctx, UIComponent parent) throws IOException {
ctx.setVariableMapper(new VariableMapperWrapper(orig));
try {
nextHandler.apply(ctx, null);
- WebConfiguration webConfig = WebConfiguration.getInstance();
- if (path.startsWith(webConfig.getOptionValue(WebConfiguration.WebContextInitParameter.WebAppContractsDirectory))) {
- throw new TagAttributeException(tag, src, "Invalid src, contract resources cannot be accessed this way : " + path);
- }
ctx.includeFacelet(parent, path);
} catch (IOException e) {
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, e.toString(), e);
}
- throw new TagAttributeException(tag, src, "Invalid path : " + path);
+ throw new TagAttributeException(tag, src, "Invalid path : " + path, e);
} finally {
ctx.setVariableMapper(orig);
}
diff --git a/impl/src/main/java/com/sun/faces/facelets/util/Classpath.java b/impl/src/main/java/com/sun/faces/facelets/util/Classpath.java
index 7359e3ef50..ed8bec58bb 100644
--- a/impl/src/main/java/com/sun/faces/facelets/util/Classpath.java
+++ b/impl/src/main/java/com/sun/faces/facelets/util/Classpath.java
@@ -16,6 +16,8 @@
package com.sun.faces.facelets.util;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -23,7 +25,6 @@
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -46,13 +47,6 @@ public final class Classpath {
private static final String[] PREFIXES_TO_EXCLUDE = { "rar:", "sar:" };
private static final String[] EXTENSIONS_TO_EXCLUDE = { ".rar", ".sar" };
- /**
- *
- */
- public Classpath() {
- super();
- }
-
public enum SearchAdvice {
FirstMatchOnly, AllMatches
}
@@ -94,7 +88,7 @@ public static URL[] search(ClassLoader cl, String prefix, String suffix, SearchA
if (jarFile != null) {
searchJar(cl, all, jarFile, prefix, suffix, advice);
} else {
- boolean searchDone = searchDir(all, new File(URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8)), suffix);
+ boolean searchDone = searchDir(all, new File(URLDecoder.decode(url.getFile(), UTF_8)), suffix);
if (!searchDone) {
searchFromURL(all, prefix, suffix, url);
}
@@ -245,7 +239,7 @@ static JarFile getAlternativeJarFile(String urlFile) throws IOException {
// And trim off any "file:" prefix.
if (jarFileUrl.startsWith("file:")) {
jarFileUrl = jarFileUrl.substring("file:".length());
- jarFileUrl = URLDecoder.decode(jarFileUrl, StandardCharsets.UTF_8);
+ jarFileUrl = URLDecoder.decode(jarFileUrl, UTF_8);
}
boolean foundExclusion = false;
for (int i = 0; i < PREFIXES_TO_EXCLUDE.length; i++) {
diff --git a/impl/src/main/java/com/sun/faces/lifecycle/ClientWindowImpl.java b/impl/src/main/java/com/sun/faces/lifecycle/ClientWindowImpl.java
index 5f1cabd651..b0d3cc3017 100644
--- a/impl/src/main/java/com/sun/faces/lifecycle/ClientWindowImpl.java
+++ b/impl/src/main/java/com/sun/faces/lifecycle/ClientWindowImpl.java
@@ -16,6 +16,7 @@
package com.sun.faces.lifecycle;
+import static com.sun.faces.context.SessionMap.getMutex;
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.CLIENT_WINDOW_PARAM;
import java.util.Map;
@@ -55,7 +56,7 @@ public void decode(FacesContext context) {
}
private String calculateClientWindow(FacesContext context) {
- synchronized (context.getExternalContext().getSession(true)) {
+ synchronized (getMutex(context.getExternalContext().getSession(true))) {
final String clientWindowCounterKey = "com.sun.faces.lifecycle.ClientWindowCounterKey";
ExternalContext extContext = context.getExternalContext();
Map sessionAttrs = extContext.getSessionMap();
diff --git a/impl/src/main/java/com/sun/faces/lifecycle/LifecycleImpl.java b/impl/src/main/java/com/sun/faces/lifecycle/LifecycleImpl.java
index 42a6b9577a..9811f3620a 100644
--- a/impl/src/main/java/com/sun/faces/lifecycle/LifecycleImpl.java
+++ b/impl/src/main/java/com/sun/faces/lifecycle/LifecycleImpl.java
@@ -66,46 +66,18 @@ public class LifecycleImpl extends Lifecycle {
// List for registered PhaseListeners
private List listeners = new CopyOnWriteArrayList<>();
- private boolean isClientWindowEnabled = false;
- private WebConfiguration config;
public LifecycleImpl() {
-
}
public LifecycleImpl(FacesContext context) {
- ExternalContext extContext = context.getExternalContext();
- config = WebConfiguration.getInstance(extContext);
- context.getApplication().subscribeToEvent(PostConstructApplicationEvent.class, Application.class, new PostConstructApplicationListener());
-
- }
-
- private class PostConstructApplicationListener implements SystemEventListener {
-
- @Override
- public boolean isListenerForSource(Object source) {
- return source instanceof Application;
- }
-
- @Override
- public void processEvent(SystemEvent event) throws AbortProcessingException {
- postConstructApplicationInitialization();
- }
-
}
- private void postConstructApplicationInitialization() {
- String optionValue = config.getOptionValue(WebConfiguration.WebContextInitParameter.ClientWindowMode);
- isClientWindowEnabled = null != optionValue && !optionValue.equals(WebConfiguration.WebContextInitParameter.ClientWindowMode.getDefaultValue());
- }
// ------------------------------------------------------- Lifecycle Methods
@Override
public void attachWindow(FacesContext context) {
- if (!isClientWindowEnabled) {
- return;
- }
if (context == null) {
throw new NullPointerException(MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"));
}
diff --git a/impl/src/main/java/com/sun/faces/lifecycle/RestoreViewPhase.java b/impl/src/main/java/com/sun/faces/lifecycle/RestoreViewPhase.java
index a869c08382..74af50b913 100644
--- a/impl/src/main/java/com/sun/faces/lifecycle/RestoreViewPhase.java
+++ b/impl/src/main/java/com/sun/faces/lifecycle/RestoreViewPhase.java
@@ -25,13 +25,14 @@
import static com.sun.faces.util.MessageUtils.getExceptionMessageString;
import static com.sun.faces.util.Util.getViewHandler;
import static com.sun.faces.util.Util.isOneOf;
+import static jakarta.faces.component.visit.VisitHint.SKIP_ITERATION;
import static jakarta.faces.event.PhaseId.RESTORE_VIEW;
import static jakarta.faces.render.ResponseStateManager.NON_POSTBACK_VIEW_TOKEN_PARAM;
import static jakarta.faces.view.ViewMetadata.hasMetadata;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.SEVERE;
-import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
@@ -80,7 +81,7 @@ public class RestoreViewPhase extends Phase {
private static final String WEBAPP_ERROR_PAGE_MARKER = "jakarta.servlet.error.message";
private static final Logger LOGGER = FacesLogger.LIFECYCLE.getLogger();
- private static String SKIP_ITERATION_HINT = "jakarta.faces.visit.SKIP_ITERATION";
+ private static final Set SKIP_ITERATION_HINT = EnumSet.of(SKIP_ITERATION);
// ---------------------------------------------------------- Public Methods
@@ -101,7 +102,7 @@ public void doPhase(FacesContext context, Lifecycle lifecycle, ListIterator
- *
+ *
* POSTCONDITION: The facesContext has been initialized with a tree.
*/
@@ -248,16 +249,7 @@ private void maybeTakeProtectedViewAction(FacesContext context, ViewHandler view
String incomingSecretKeyValue = extContext.getRequestParameterMap().get(NON_POSTBACK_VIEW_TOKEN_PARAM);
if (incomingSecretKeyValue != null) {
- try {
- incomingSecretKeyValue = URLEncoder.encode(incomingSecretKeyValue, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- if (LOGGER.isLoggable(SEVERE)) {
- LOGGER.log(SEVERE,
- "Unable to re-encode value of request parameter " + NON_POSTBACK_VIEW_TOKEN_PARAM + ":"
- + incomingSecretKeyValue, e);
- }
- incomingSecretKeyValue = null;
- }
+ incomingSecretKeyValue = URLEncoder.encode(incomingSecretKeyValue, UTF_8);
}
String correctSecretKeyValue = rsm.getCryptographicallyStrongTokenFromSession(context);
@@ -382,14 +374,8 @@ private void deliverPostRestoreStateEvent(FacesContext facesContext) throws Face
UIViewRoot root = facesContext.getViewRoot();
PostRestoreStateEvent postRestoreStateEvent = new PostRestoreStateEvent(root);
try {
- // PENDING: This is included for those component frameworks that don't utilize the
- // new VisitHint(s) yet - but still wish to know that they should be non-iterating
- // during state saving. It should be removed at some point.
- facesContext.getAttributes().put(SKIP_ITERATION_HINT, true);
facesContext.getApplication().publishEvent(facesContext, PostRestoreStateEvent.class, root);
-
- Set hints = EnumSet.of(VisitHint.SKIP_ITERATION);
- VisitContext visitContext = VisitContext.createVisitContext(facesContext, null, hints);
+ VisitContext visitContext = VisitContext.createVisitContext(facesContext, null, SKIP_ITERATION_HINT);
root.visitTree(visitContext, (context, target) -> {
postRestoreStateEvent.setComponent(target);
target.processEvent(postRestoreStateEvent);
@@ -401,11 +387,6 @@ private void deliverPostRestoreStateEvent(FacesContext facesContext) throws Face
.publishEvent(
facesContext, ExceptionQueuedEvent.class,
new ExceptionQueuedEventContext(facesContext, e, null, PhaseId.RESTORE_VIEW));
- } finally {
- // PENDING: This is included for those component frameworks that don't utilize the
- // new VisitHint(s) yet - but still wish to know that they should be non-iterating
- // during state saving. It should be removed at some point.
- facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
}
}
diff --git a/impl/src/main/java/com/sun/faces/renderkit/RenderKitUtils.java b/impl/src/main/java/com/sun/faces/renderkit/RenderKitUtils.java
index 36b57a3b43..a1bbd394cd 100644
--- a/impl/src/main/java/com/sun/faces/renderkit/RenderKitUtils.java
+++ b/impl/src/main/java/com/sun/faces/renderkit/RenderKitUtils.java
@@ -37,6 +37,7 @@
import java.util.logging.Logger;
import com.sun.faces.RIConstants;
+import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.el.ELUtils;
import com.sun.faces.facelets.util.DevTools;
@@ -1219,9 +1220,8 @@ public static String getImageSource(FacesContext context, UIComponent component,
ResourceHandler handler = context.getApplication().getResourceHandler();
if (resName != null) {
String libName = (String) component.getAttributes().get("library");
- WebConfiguration webConfig = WebConfiguration.getInstance();
-
- if (libName == null && resName.startsWith(webConfig.getOptionValue(WebConfiguration.WebContextInitParameter.WebAppContractsDirectory))) {
+
+ if (libName == null && ApplicationAssociate.getInstance(context).getResourceManager().isContractsResource(resName)) {
if (context.isProjectStage(ProjectStage.Development)) {
String msg = "Illegal path, direct contract references are not allowed: " + resName;
context.addMessage(component.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg));
diff --git a/impl/src/main/java/com/sun/faces/renderkit/ServerSideStateHelper.java b/impl/src/main/java/com/sun/faces/renderkit/ServerSideStateHelper.java
index 5e0e271293..5c96f298aa 100644
--- a/impl/src/main/java/com/sun/faces/renderkit/ServerSideStateHelper.java
+++ b/impl/src/main/java/com/sun/faces/renderkit/ServerSideStateHelper.java
@@ -22,6 +22,7 @@
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.SerializeServerState;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.NumberOfLogicalViews;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.NumberOfViews;
+import static com.sun.faces.context.SessionMap.getMutex;
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.VIEW_STATE_PARAM;
import static com.sun.faces.util.Util.notNull;
import static java.util.logging.Level.FINE;
@@ -149,7 +150,7 @@ public void writeState(FacesContext ctx, Object state, StringBuilder stateCaptur
Object sessionObj = externalContext.getSession(true);
Map sessionMap = externalContext.getSessionMap();
- synchronized (sessionObj) {
+ synchronized (getMutex(sessionObj)) {
Map logicalMap = TypedCollections.dynamicallyCastMap((Map) sessionMap.get(LOGICAL_VIEW_MAP), String.class, Map.class);
if (logicalMap == null) {
logicalMap = Collections.synchronizedMap(new LRUMap(numberOfLogicalViews));
@@ -262,7 +263,7 @@ public Object getState(FacesContext ctx, String viewId) {
}
// noinspection SynchronizationOnLocalVariableOrMethodParameter
- synchronized (sessionObj) {
+ synchronized (getMutex(sessionObj)) {
Map logicalMap = (Map) externalCtx.getSessionMap().get(LOGICAL_VIEW_MAP);
if (logicalMap != null) {
Map actualMap = (Map) logicalMap.get(idInLogicalMap);
@@ -328,7 +329,7 @@ protected Object handleSaveState(Object state) {
if (!webConfig.isOptionEnabled(SerializeServerState)) {
return state;
}
-
+
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
ObjectOutputStream oas = null;
try {
@@ -346,7 +347,7 @@ protected Object handleSaveState(Object state) {
}
}
}
-
+
return baos.toByteArray();
}
@@ -359,7 +360,7 @@ protected Object handleRestoreState(Object state) {
if (!webConfig.isOptionEnabled(SerializeServerState)) {
return state;
}
-
+
try (ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) state);
ObjectInputStream ois = serialProvider.createObjectInputStream(compressViewState ? new GZIPInputStream(bais, 1024) : bais);) {
return ois.readObject();
@@ -402,7 +403,7 @@ public boolean isStateless(FacesContext facesContext, String viewId) throws Ille
if (!facesContext.isPostback()) {
throw new IllegalStateException("Cannot determine whether or not the request is stateless");
}
-
+
String compoundId = getStateParamValue(facesContext);
return compoundId != null && "stateless".equals(compoundId);
}
diff --git a/impl/src/main/java/com/sun/faces/renderkit/StateHelper.java b/impl/src/main/java/com/sun/faces/renderkit/StateHelper.java
index fb53004d6f..b865c9d165 100644
--- a/impl/src/main/java/com/sun/faces/renderkit/StateHelper.java
+++ b/impl/src/main/java/com/sun/faces/renderkit/StateHelper.java
@@ -21,10 +21,10 @@
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.CLIENT_WINDOW_PARAM;
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.RENDER_KIT_ID_PARAM;
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.VIEW_STATE_PARAM;
+import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
import java.util.logging.Logger;
import com.sun.faces.RIConstants;
@@ -112,7 +112,7 @@ public static void createAndStoreCryptographicallyStrongTokenInSession(HttpSessi
ByteArrayGuardAESCTR guard = new ByteArrayGuardAESCTR();
String clearText = String.valueOf(System.currentTimeMillis());
String result = guard.encrypt(clearText);
- result = URLEncoder.encode(result, StandardCharsets.UTF_8);
+ result = URLEncoder.encode(result, UTF_8);
session.setAttribute(TOKEN_NAME, result);
}
@@ -167,7 +167,7 @@ protected static String getStateParamValue(FacesContext context) {
if (pValue != null && pValue.length() == 0) {
pValue = null;
}
-
+
return pValue;
}
diff --git a/impl/src/main/java/com/sun/faces/renderkit/html_basic/AjaxBehaviorRenderer.java b/impl/src/main/java/com/sun/faces/renderkit/html_basic/AjaxBehaviorRenderer.java
index 7fea1c875a..31842eff93 100644
--- a/impl/src/main/java/com/sun/faces/renderkit/html_basic/AjaxBehaviorRenderer.java
+++ b/impl/src/main/java/com/sun/faces/renderkit/html_basic/AjaxBehaviorRenderer.java
@@ -26,7 +26,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
-import com.sun.faces.facelets.tag.composite.InsertChildrenHandler;
+import com.sun.faces.facelets.tag.composite.RetargetedAjaxBehavior;
import com.sun.faces.renderkit.RenderKitUtils;
import com.sun.faces.util.FacesLogger;
@@ -221,9 +221,9 @@ private static String buildAjaxCommand(ClientBehaviorContext behaviorContext, Aj
ajaxCommand.append(eventName);
ajaxCommand.append("',");
- appendIds(behaviorContext.getFacesContext(), component, ajaxCommand, execute);
+ appendIds(behaviorContext.getFacesContext(), component, ajaxBehavior, ajaxCommand, execute);
ajaxCommand.append(",");
- appendIds(behaviorContext.getFacesContext(), component, ajaxCommand, render);
+ appendIds(behaviorContext.getFacesContext(), component, ajaxBehavior, ajaxCommand, render);
if (onevent != null || onerror != null || delay != null || resetValues != null || !params.isEmpty()) {
@@ -280,7 +280,7 @@ private static String buildAjaxCommand(ClientBehaviorContext behaviorContext, Aj
SearchExpressionHint.RESOLVE_SINGLE_COMPONENT);
// Appends an ids argument to the ajax command
- private static void appendIds(FacesContext facesContext, UIComponent component, StringBuilder builder, Collection ids) {
+ private static void appendIds(FacesContext facesContext, UIComponent component, AjaxBehavior ajaxBehavior, StringBuilder builder, Collection ids) {
if (null == ids || ids.isEmpty()) {
builder.append('0');
@@ -311,7 +311,7 @@ private static void appendIds(FacesContext facesContext, UIComponent component,
boolean clientResolveableExpression = expression.equals("@all") || expression.equals("@none") || expression.equals("@form") || expression.equals("@this");
- if (composite != null && !isHandledByInsertChildren(component, composite) && (expression.equals("@this") || expression.startsWith("@this" + separatorChar))) {
+ if (composite != null && (ajaxBehavior instanceof RetargetedAjaxBehavior) && (expression.equals("@this") || expression.startsWith("@this" + separatorChar))) {
expression = expression.replaceFirst("@this", separatorChar + composite.getClientId(facesContext));
clientResolveableExpression = false;
}
@@ -347,16 +347,6 @@ private static void appendIds(FacesContext facesContext, UIComponent component,
builder.append("'");
}
- private static boolean isHandledByInsertChildren(UIComponent component, UIComponent composite) {
- for (UIComponent parent = component.getParent(); parent != null && !parent.equals(composite); parent = parent.getParent()) {
- if (parent.getAttributes().containsKey(InsertChildrenHandler.INDEX_ATTRIBUTE)) {
- return true;
- }
- }
-
- return false;
- }
-
// Returns the resolved (client id) for a particular id.
private static String getResolvedId(UIComponent component, String id) {
diff --git a/impl/src/main/java/com/sun/faces/renderkit/html_basic/OutputLinkRenderer.java b/impl/src/main/java/com/sun/faces/renderkit/html_basic/OutputLinkRenderer.java
index 2232224579..20d155f4d7 100644
--- a/impl/src/main/java/com/sun/faces/renderkit/html_basic/OutputLinkRenderer.java
+++ b/impl/src/main/java/com/sun/faces/renderkit/html_basic/OutputLinkRenderer.java
@@ -19,11 +19,11 @@
package com.sun.faces.renderkit.html_basic;
import static com.sun.faces.util.Util.componentIsDisabled;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.logging.Level.FINE;
import java.io.IOException;
import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
import com.sun.faces.renderkit.Attribute;
import com.sun.faces.renderkit.AttributeManager;
@@ -36,7 +36,7 @@
/**
* OutputLinkRenderer is a class ...
- *
+ *
* Lifetime And Scope
*
*/
@@ -134,7 +134,7 @@ protected Object getValue(UIComponent component) {
if (componentIsDisabled(component)) {
return null;
}
-
+
return ((UIOutput) component).getValue();
}
@@ -177,10 +177,10 @@ protected void renderAsActive(FacesContext context, UIComponent component) throw
if (pn != null && pn.length() != 0) {
String pv = param.value;
sb.append(paramWritten ? '&' : '?');
- sb.append(URLEncoder.encode(pn, StandardCharsets.UTF_8));
+ sb.append(URLEncoder.encode(pn, UTF_8));
sb.append('=');
if (pv != null && pv.length() != 0) {
- sb.append(URLEncoder.encode(pv, StandardCharsets.UTF_8));
+ sb.append(URLEncoder.encode(pv, UTF_8));
}
paramWritten = true;
}
diff --git a/impl/src/main/java/com/sun/faces/renderkit/html_basic/ScriptStyleBaseRenderer.java b/impl/src/main/java/com/sun/faces/renderkit/html_basic/ScriptStyleBaseRenderer.java
index 39dc2b9fd9..838c722ae8 100644
--- a/impl/src/main/java/com/sun/faces/renderkit/html_basic/ScriptStyleBaseRenderer.java
+++ b/impl/src/main/java/com/sun/faces/renderkit/html_basic/ScriptStyleBaseRenderer.java
@@ -21,7 +21,7 @@
import java.util.Map;
import java.util.logging.Logger;
-import com.sun.faces.config.WebConfiguration;
+import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.util.FacesLogger;
import jakarta.faces.application.FacesMessage;
@@ -194,9 +194,7 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce
ResponseWriter writer = context.getResponseWriter();
startExternalElement(context, writer, component);
- WebConfiguration webConfig = WebConfiguration.getInstance();
-
- if (library == null && name != null && name.startsWith(webConfig.getOptionValue(WebConfiguration.WebContextInitParameter.WebAppContractsDirectory))) {
+ if (library == null && name != null && ApplicationAssociate.getInstance(context).getResourceManager().isContractsResource(name)) {
if (context.isProjectStage(ProjectStage.Development)) {
String msg = "Illegal path, direct contract references are not allowed: " + name;
context.addMessage(component.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg));
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 64bc853a06..d445f08854 100644
--- a/impl/src/main/java/com/sun/faces/util/Util.java
+++ b/impl/src/main/java/com/sun/faces/util/Util.java
@@ -19,16 +19,20 @@
package com.sun.faces.util;
+import static com.sun.faces.RIConstants.FACELETS_ENCODING_KEY;
import static com.sun.faces.RIConstants.FACES_SERVLET_MAPPINGS;
import static com.sun.faces.RIConstants.FACES_SERVLET_REGISTRATION;
+import static com.sun.faces.RIConstants.NO_VALUE;
import static com.sun.faces.util.MessageUtils.ILLEGAL_ATTEMPT_SETTING_APPLICATION_ARTIFACT_ID;
import static com.sun.faces.util.MessageUtils.NAMED_OBJECT_NOT_FOUND_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.NULL_VIEW_ID_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.getExceptionMessageString;
+import static jakarta.faces.application.ViewHandler.CHARACTER_ENCODING_KEY;
import static java.lang.Character.isDigit;
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 java.beans.FeatureDescriptor;
@@ -64,6 +68,7 @@
import javax.naming.InitialContext;
import javax.naming.NamingException;
+import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -110,7 +115,7 @@
/**
* Util is a class ...
- *
+ *
* Lifetime And Scope
*
*/
@@ -274,12 +279,35 @@ public static boolean isUnitTestModeEnabled() {
return unitTestModeEnabled;
}
+ public static interface ThrowingBiConsumer {
+ void accept(T t, U u) throws Exception;
+ }
+
+ private static void setFeature(ThrowingBiConsumer setter, F feature, Boolean flag) {
+ try {
+ setter.accept(feature, flag);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("The feature '" + feature + "' is not supported by your XML processor.", e);
+ }
+ }
+
+ private static void setPossiblyUnsupportedFeature(ThrowingBiConsumer setter, F feature, Boolean flag) {
+ try {
+ setFeature(setter, feature, flag);
+ } catch (IllegalArgumentException e) {
+ LOGGER.log(Level.FINE, e.getMessage(), e);
+ }
+ }
+
public static TransformerFactory createTransformerFactory() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
TransformerFactory factory;
try {
Thread.currentThread().setContextClassLoader(Util.class.getClassLoader());
factory = TransformerFactory.newInstance();
+ factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, NO_VALUE);
+ factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, NO_VALUE);
+ setFeature(factory::setFeature, XMLConstants.FEATURE_SECURE_PROCESSING, true);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
@@ -303,13 +331,25 @@ public static DocumentBuilderFactory createDocumentBuilderFactory() {
DocumentBuilderFactory factory;
try {
Thread.currentThread().setContextClassLoader(Util.class.getClassLoader());
- factory = DocumentBuilderFactory.newInstance();
+ factory = createLocalDocumentBuilderFactory();
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
return factory;
}
+ public static DocumentBuilderFactory createLocalDocumentBuilderFactory() {
+ DocumentBuilderFactory factory;
+ factory = DocumentBuilderFactory.newInstance();
+ factory.setXIncludeAware(false);
+ factory.setExpandEntityReferences(false);
+ setFeature(factory::setFeature, XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ setPossiblyUnsupportedFeature(factory::setFeature, "http://xml.org/sax/features/external-general-entities", false);
+ setPossiblyUnsupportedFeature(factory::setFeature, "http://xml.org/sax/features/external-parameter-entities", false);
+ setPossiblyUnsupportedFeature(factory::setFeature, "http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ return factory;
+ }
+
public static SchemaFactory createSchemaFactory(String uri) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
SchemaFactory factory;
@@ -1650,4 +1690,80 @@ public static int extractFirstNumericSegment(String clientId, char separatorChar
throw new NumberFormatException("there is no numeric segment");
}
+ /**
+ * @param context the {@link FacesContext} for the current request
+ * @return the encoding to be used for the response
+ */
+ public static String getResponseEncoding(FacesContext context) {
+ return getResponseEncoding(context, Optional.empty());
+ }
+
+ /**
+ * @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 defaultEncoding) {
+
+ // 1. First get it from viewroot, if any.
+ if (context.getViewRoot() != null) {
+ String encoding = (String) context.getViewRoot().getAttributes().get(FACELETS_ENCODING_KEY);
+
+ if (encoding != null) {
+ // If found, then immediately return it, this represents either the encoding explicitly set via or the one actually set on response.
+ // See also ViewHandler#apply() and FaceletViewHandlingStrategy#createResponseWriter().
+ return encoding;
+ }
+ }
+
+ // 2. If none found then get it from context (this is usually set during compile/buildtime based on request character encoding).
+ // See also SAXCompiler#doCompile() and EncodingHandler#apply().
+ String encoding = (String) context.getAttributes().get(FACELETS_ENCODING_KEY);
+
+ if (encoding != null && LOGGER.isLoggable(FINEST)) {
+ LOGGER.log(FINEST, "Using Facelet encoding {0}", encoding);
+ }
+
+ if (encoding == null) {
+ // 3. If none found then get it from request (could happen when the view isn't built yet).
+ // See also ViewHandler#initView() and ViewHandler#calculateCharacterEncoding().
+ encoding = context.getExternalContext().getRequestCharacterEncoding();
+
+ if (encoding != null && LOGGER.isLoggable(FINEST)) {
+ LOGGER.log(FINEST, "Using Request encoding {0}", encoding);
+ }
+ }
+
+ 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().
+ encoding = (String) context.getExternalContext().getSessionMap().get(CHARACTER_ENCODING_KEY);
+
+ if (encoding != null && LOGGER.isLoggable(FINEST)) {
+ LOGGER.log(FINEST, "Using Session encoding {0}", encoding);
+ }
+ }
+
+ if (encoding == null) {
+ // 5. If still none found then fall back to specified default.
+ encoding = defaultEncoding.get();
+
+ if (encoding != null && !encoding.isBlank()) {
+ if (LOGGER.isLoggable(FINEST)) {
+ LOGGER.log(FINEST, "Using specified default encoding {0}", encoding);
+ }
+ }
+ else {
+ // 6. If specified default is null or blank then finally fall back to hardcoded default.
+ encoding = RIConstants.CHAR_ENCODING;
+
+ if (LOGGER.isLoggable(FINEST)) {
+ LOGGER.log(FINEST, "No encoding found, defaulting to {0}", encoding);
+ }
+ }
+ }
+
+ return encoding;
+ }
+
}
diff --git a/impl/src/main/java/jakarta/faces/application/ViewHandler.java b/impl/src/main/java/jakarta/faces/application/ViewHandler.java
index d4c868349a..9baecf1532 100644
--- a/impl/src/main/java/jakarta/faces/application/ViewHandler.java
+++ b/impl/src/main/java/jakarta/faces/application/ViewHandler.java
@@ -203,12 +203,17 @@ public abstract class ViewHandler {
*
*/
public void initView(FacesContext context) throws FacesException {
- String encoding = context.getExternalContext().getRequestCharacterEncoding();
- if (encoding != null) {
+ String originalEncoding = context.getExternalContext().getRequestCharacterEncoding();
+ String encoding = (originalEncoding != null) ? originalEncoding : calculateCharacterEncoding(context);
+
+ if (encoding != null && context.getExternalContext().getSession(false) != null) {
+ context.getExternalContext().getSessionMap().put(CHARACTER_ENCODING_KEY, encoding);
+ }
+
+ if (originalEncoding != null) {
return;
}
- encoding = calculateCharacterEncoding(context);
if (encoding != null) {
try {
context.getExternalContext().setRequestCharacterEncoding(encoding);
@@ -466,7 +471,7 @@ public String deriveLogicalViewId(FacesContext context, String requestViewId) {
*
* Return a Jakarta Faces action URL derived from the viewId
argument that is suitable to be used by
* the {@link NavigationHandler} to issue a redirect request to the URL using a NonFaces request. Compliant
- * implementations must implement this method as specified in
+ * implementations must implement this method as specified in
* section 7.6.2 "Default ViewHandler Implementation" of the Jakarta Faces Specification Document.
* The default implementation simply calls
* through to {@link #getActionURL}, passing the arguments context
and viewId
.
diff --git a/impl/src/main/java/jakarta/faces/component/UIData.java b/impl/src/main/java/jakarta/faces/component/UIData.java
index 9c38a0338b..16d8dfc235 100644
--- a/impl/src/main/java/jakarta/faces/component/UIData.java
+++ b/impl/src/main/java/jakarta/faces/component/UIData.java
@@ -1013,8 +1013,14 @@ public void broadcast(FacesEvent event) throws AbortProcessingException {
if (isNestedWithinIterator(context)) {
setDataModel(null);
}
- int oldRowIndex = getRowIndex();
- setRowIndex(revent.getRowIndex());
+ int currentRowIndex = getRowIndex();
+ int broadcastedRowIndex = revent.getRowIndex();
+ boolean needsToSetIndex = currentRowIndex != -1 || broadcastedRowIndex != -1; // #5213
+
+ if (needsToSetIndex) {
+ setRowIndex(broadcastedRowIndex);
+ }
+
FacesEvent rowEvent = revent.getFacesEvent();
UIComponent source = rowEvent.getComponent();
UIComponent compositeParent = null;
@@ -1033,8 +1039,10 @@ public void broadcast(FacesEvent event) throws AbortProcessingException {
compositeParent.popComponentFromEL(context);
}
}
- setRowIndex(oldRowIndex);
+ if (needsToSetIndex) {
+ setRowIndex(currentRowIndex);
+ }
}
/**
diff --git a/impl/src/main/java/jakarta/faces/component/UIInput.java b/impl/src/main/java/jakarta/faces/component/UIInput.java
index ad8ad72001..92f84fd447 100644
--- a/impl/src/main/java/jakarta/faces/component/UIInput.java
+++ b/impl/src/main/java/jakarta/faces/component/UIInput.java
@@ -252,7 +252,7 @@ public String getFamily() {
*/
@Override
public Object getSubmittedValue() {
- if (submittedValue == null && !isValid() && considerEmptyStringNull(FacesContext.getCurrentInstance())) { // JAVASERVERFACES_SPEC_PUBLIC-671
+ if (submittedValue == null && !isValid() && !isLocalValueSet() && considerEmptyStringNull(FacesContext.getCurrentInstance())) { // JAVASERVERFACES_SPEC_PUBLIC-671
return "";
} else {
return submittedValue;
diff --git a/impl/src/main/java/jakarta/faces/context/PartialResponseWriter.java b/impl/src/main/java/jakarta/faces/context/PartialResponseWriter.java
index aa4b4cf893..49a6427519 100644
--- a/impl/src/main/java/jakarta/faces/context/PartialResponseWriter.java
+++ b/impl/src/main/java/jakarta/faces/context/PartialResponseWriter.java
@@ -17,8 +17,11 @@
package jakarta.faces.context;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.Map;
+import com.sun.faces.RIConstants;
+
import jakarta.faces.component.NamingContainer;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.render.ResponseStateManager;
@@ -97,7 +100,7 @@ public void startDocument() throws IOException {
ResponseWriter writer = getWrapped();
String encoding = writer.getCharacterEncoding();
if (encoding == null) {
- encoding = "utf-8";
+ encoding = RIConstants.CHAR_ENCODING;
}
writer.writePreamble("\n");
writer.startElement("partial-response", null);
diff --git a/impl/src/main/resources/com/sun/faces/LogStrings.properties b/impl/src/main/resources/com/sun/faces/LogStrings.properties
index b318c6541c..cc78b3db42 100644
--- a/impl/src/main/resources/com/sun/faces/LogStrings.properties
+++ b/impl/src/main/resources/com/sun/faces/LogStrings.properties
@@ -25,7 +25,7 @@ faces.util.no.adapter.ctor.available="JSF1016: Target class ''{0}'' doesn't have
# core tags
faces.core.tags.eval_result_not_expected_type=JSF1011: Evaluation of expression for attribute ''{0}'' resulted in unexpected type. Expected {1}, but received {2}.
faces.util_no_annotation_processed=JSF1014: Unable to load annotation class ''{0}''. As a result, this annotation will not be processed.
-faces.config.listener.version=Initializing Mojarra ${mojarra.logstrings.version} for context ''{0}''
+faces.config.listener.version=Initializing Mojarra {0} for context ''{1}''
faces.config.listener.version.complete=Completed initializing Mojarra (${mojarra.logstrings.version}) for context ''{0}''
faces.config.listener.predestroy.error=JSF1017: ''{0}'' was thrown while handling the PreDestroy annotation(s) for bean ''{1}'' in scope ''{2}''. Message: {3}
faces.viewhandler.requestpath.recursion=JSF1015: Request path ''{0}'' begins with one or more occurrences of the FacesServlet prefix path mapping ''{1}''.
diff --git a/impl/src/test/java/com/sun/faces/config/processor/FacesConfigNamespaceContextTest.java b/impl/src/test/java/com/sun/faces/config/processor/FacesConfigNamespaceContextTest.java
index d4d1150b59..cb59988b72 100644
--- a/impl/src/test/java/com/sun/faces/config/processor/FacesConfigNamespaceContextTest.java
+++ b/impl/src/test/java/com/sun/faces/config/processor/FacesConfigNamespaceContextTest.java
@@ -16,6 +16,7 @@
package com.sun.faces.config.processor;
+import static com.sun.faces.util.Util.createLocalDocumentBuilderFactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -79,7 +80,7 @@ public void testJakartaEENSWithParameter() throws ParserConfigurationException,
private Document createFacesConfig(String flowName, String namespace, String version)
throws ParserConfigurationException {
- DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilderFactory documentBuilderFactory = createLocalDocumentBuilderFactory();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document docFlowConfig = documentBuilder.newDocument();
diff --git a/impl/src/test/java/jakarta/faces/FacesConfigOrderingTestCase.java b/impl/src/test/java/jakarta/faces/FacesConfigOrderingTestCase.java
index c5c7187bfb..c072c4fd98 100644
--- a/impl/src/test/java/jakarta/faces/FacesConfigOrderingTestCase.java
+++ b/impl/src/test/java/jakarta/faces/FacesConfigOrderingTestCase.java
@@ -16,6 +16,8 @@
package jakarta.faces;
+import static com.sun.faces.util.Util.createLocalDocumentBuilderFactory;
+
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -25,10 +27,6 @@
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -36,6 +34,9 @@
import com.sun.faces.config.manager.documents.DocumentOrderingWrapper;
import jakarta.faces.context.FacesContext;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
public class FacesConfigOrderingTestCase extends TestCase {
@@ -391,7 +392,7 @@ private void populateIds(String elementName, List ids, String ns,
private Document newDocument() throws ParserConfigurationException {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilderFactory factory = createLocalDocumentBuilderFactory();
factory.setValidating(false);
factory.setNamespaceAware(true);
return factory.newDocumentBuilder().newDocument();