From db53027b3148fe37e9ceaa36b92e2bc01042abb0 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 20 May 2023 09:51:17 -0400 Subject: [PATCH 01/44] Fix #5232: fail fast when CDI is unavailable --- impl/src/main/java/com/sun/faces/config/FacesInitializer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/impl/src/main/java/com/sun/faces/config/FacesInitializer.java b/impl/src/main/java/com/sun/faces/config/FacesInitializer.java index e56a2bc78d..b38534d356 100644 --- a/impl/src/main/java/com/sun/faces/config/FacesInitializer.java +++ b/impl/src/main/java/com/sun/faces/config/FacesInitializer.java @@ -27,6 +27,8 @@ import java.util.Map; import java.util.Set; +import com.sun.faces.util.Util; + import jakarta.faces.annotation.FacesConfig; import jakarta.faces.application.ResourceDependencies; import jakarta.faces.application.ResourceDependency; @@ -104,6 +106,7 @@ public void onStartup(Set> classes, ServletContext servletContext) thro if (appHasFacesContent || appHasFacesServlet) { addAnnotatedClasses(classes, servletContext); InitFacesContext initFacesContext = new InitFacesContext(servletContext); + Util.getCdiBeanManager(initFacesContext); // #5232 Fail fast when CDI is really not available. try { if (appHasFacesContent) { From db7f686b4e31a759d70af17eae472c16f823cb86 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 20 May 2023 09:51:51 -0400 Subject: [PATCH 02/44] Fix #5232: force Weld to reinitialize when it skipped initialization (usually due to empty BDA) --- impl/src/main/java/com/sun/faces/util/Util.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 cacd9f20dc..e6dcb980e7 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -30,6 +30,7 @@ import static java.util.Collections.emptyList; import static java.util.logging.Level.FINE; import static java.util.logging.Level.SEVERE; +import static java.util.logging.Level.WARNING; import java.beans.FeatureDescriptor; import java.io.IOException; @@ -100,6 +101,7 @@ import jakarta.faces.event.AbortProcessingException; import jakarta.faces.render.ResponseStateManager; import jakarta.faces.webapp.FacesServlet; +import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRegistration; import jakarta.servlet.http.HttpServletMapping; @@ -1482,6 +1484,19 @@ public static BeanManager getCdiBeanManager(FacesContext facesContext) { if (result == null && facesContext != null) { Map applicationMap = facesContext.getExternalContext().getApplicationMap(); result = (BeanManager) applicationMap.get("org.jboss.weld.environment.servlet.jakarta.enterprise.inject.spi.BeanManager"); + + if (result == null && applicationMap.get("org.jboss.weld.environment.servlet.enhancedListenerUsed") == Boolean.TRUE) { + LOGGER.log(WARNING, "Weld skipped initialization - forcing it to reinitialize"); + try { + ServletContext servletContext = (ServletContext) facesContext.getExternalContext().getContext(); + servletContext.setInitParameter("org.jboss.weld.environment.servlet.archive.isolation", "false"); + ServletContainerInitializer weld = (ServletContainerInitializer) Class.forName("org.jboss.weld.environment.servlet.EnhancedListener").getConstructor().newInstance(); + weld.onStartup(null, servletContext); + result = (BeanManager) applicationMap.get("org.jboss.weld.environment.servlet.jakarta.enterprise.inject.spi.BeanManager"); + } catch (Exception | LinkageError e) { + LOGGER.log(WARNING, "Reinitializing Weld failed", e); + } + } } if (result != null && facesContext != null) { From bda4b77b6bfbc1e087ee1f832bbac5a38bc3036a Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 20 May 2023 10:01:13 -0400 Subject: [PATCH 03/44] Improve #5232: add hint how to fix Weld init fail on your own --- impl/src/main/java/com/sun/faces/util/Util.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e6dcb980e7..8e14d63b11 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -1494,7 +1494,7 @@ public static BeanManager getCdiBeanManager(FacesContext facesContext) { weld.onStartup(null, servletContext); result = (BeanManager) applicationMap.get("org.jboss.weld.environment.servlet.jakarta.enterprise.inject.spi.BeanManager"); } catch (Exception | LinkageError e) { - LOGGER.log(WARNING, "Reinitializing Weld failed", e); + LOGGER.log(WARNING, "Reinitializing Weld failed - giving up, please make sure your project has at least one @Named bean and retry", e); } } } From 8d59b3de2b64229299f04880fd75310f03aafda0 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 9 Jul 2023 10:41:07 -0400 Subject: [PATCH 04/44] https://github.com/eclipse-ee4j/mojarra/issues/5232 Improved warning message --- impl/src/main/java/com/sun/faces/util/Util.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 438ae16ee8..48e26e1d96 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -1486,7 +1486,7 @@ public static BeanManager getCdiBeanManager(FacesContext facesContext) { weld.onStartup(null, servletContext); result = (BeanManager) applicationMap.get("org.jboss.weld.environment.servlet.jakarta.enterprise.inject.spi.BeanManager"); } catch (Exception | LinkageError e) { - LOGGER.log(WARNING, "Reinitializing Weld failed - giving up, please make sure your project has at least one @Named bean and retry", e); + LOGGER.log(WARNING, "Reinitializing Weld failed - giving up, please make sure your project contains at least one bean class with a bean defining annotation and retry", e); } } } From d324508fe83987d9559c717511c4d4cf6a9759f1 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 9 Jul 2023 11:50:43 -0400 Subject: [PATCH 05/44] https://github.com/eclipse-ee4j/mojarra/issues/5232 'Fail fast' line should be in ConfigureListener because ordering of ServletContainerInitializer is undefined --- impl/src/main/java/com/sun/faces/config/ConfigureListener.java | 1 + impl/src/main/java/com/sun/faces/config/FacesInitializer.java | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/config/ConfigureListener.java b/impl/src/main/java/com/sun/faces/config/ConfigureListener.java index fa975e1f73..684baa88bb 100644 --- a/impl/src/main/java/com/sun/faces/config/ConfigureListener.java +++ b/impl/src/main/java/com/sun/faces/config/ConfigureListener.java @@ -121,6 +121,7 @@ public void contextInitialized(ServletContextEvent servletContextEvent) { } InitFacesContext initFacesContext = new InitFacesContext(servletContext); + Util.getCdiBeanManager(initFacesContext); // #5232 Fail fast when CDI is really not available. LOGGER.log(FINE, () -> format("ConfigureListener.contextInitialized({0})", servletContext.getContextPath())); diff --git a/impl/src/main/java/com/sun/faces/config/FacesInitializer.java b/impl/src/main/java/com/sun/faces/config/FacesInitializer.java index b38534d356..e56a2bc78d 100644 --- a/impl/src/main/java/com/sun/faces/config/FacesInitializer.java +++ b/impl/src/main/java/com/sun/faces/config/FacesInitializer.java @@ -27,8 +27,6 @@ import java.util.Map; import java.util.Set; -import com.sun.faces.util.Util; - import jakarta.faces.annotation.FacesConfig; import jakarta.faces.application.ResourceDependencies; import jakarta.faces.application.ResourceDependency; @@ -106,7 +104,6 @@ public void onStartup(Set> classes, ServletContext servletContext) thro if (appHasFacesContent || appHasFacesServlet) { addAnnotatedClasses(classes, servletContext); InitFacesContext initFacesContext = new InitFacesContext(servletContext); - Util.getCdiBeanManager(initFacesContext); // #5232 Fail fast when CDI is really not available. try { if (appHasFacesContent) { From 4f8d049563d59159d1e1a2ce375f8dab6a27b663 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 23 Jul 2023 10:45:55 -0400 Subject: [PATCH 06/44] https://github.com/eclipse-ee4j/mojarra/issues/5213 RowIndex/DataModel of UIData doesn't need to be reset when broadcasted row index is same as current row index; noticed same thing on UIRepeat as well so also improved over there --- .../com/sun/faces/facelets/component/UIRepeat.java | 10 +++++++--- .../main/java/javax/faces/component/UIData.java | 14 ++++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) 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 cd26b01803..7eb37027bd 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 @@ -967,13 +967,15 @@ public void broadcast(FacesEvent event) throws AbortProcessingException { FacesEvent target = idxEvent.getTarget(); FacesContext ctx = target.getFacesContext(); this.resetDataModel(ctx); + int idx = idxEvent.getIndex(); int prevIndex = this.index; UIComponent source = target.getComponent(); UIComponent compositeParent = null; try { int rowCount = getDataModel().getRowCount(); - int idx = idxEvent.getIndex(); - this.setIndex(ctx, idx); + if (idx != prevIndex) { + this.setIndex(ctx, idx); + } Integer begin = this.getBegin(); Integer end = this.getEnd(); Integer step = this.getStep(); @@ -1005,7 +1007,9 @@ public void broadcast(FacesEvent event) throws AbortProcessingException { compositeParent.popComponentFromEL(ctx); } this.updateIterationStatus(ctx, null); - this.setIndex(ctx, prevIndex); + if (idx != prevIndex) { + this.setIndex(ctx, prevIndex); + } } } else { super.broadcast(event); diff --git a/impl/src/main/java/javax/faces/component/UIData.java b/impl/src/main/java/javax/faces/component/UIData.java index c0e1157bbb..e32744076e 100644 --- a/impl/src/main/java/javax/faces/component/UIData.java +++ b/impl/src/main/java/javax/faces/component/UIData.java @@ -18,7 +18,6 @@ import static com.sun.faces.util.Util.extractFirstNumericSegment; import static com.sun.faces.util.Util.isNestedInIterator; -import static java.lang.Character.isDigit; import java.io.IOException; import java.io.Serializable; @@ -1102,8 +1101,13 @@ public void broadcast(FacesEvent event) if (isNestedWithinIterator(context)) { setDataModel(null); } - int oldRowIndex = getRowIndex(); - setRowIndex(revent.getRowIndex()); + int currentRowIndex = getRowIndex(); + int broadcastedRowIndex = revent.getRowIndex(); + + if (currentRowIndex != broadcastedRowIndex) { + setRowIndex(broadcastedRowIndex); + } + FacesEvent rowEvent = revent.getFacesEvent(); UIComponent source = rowEvent.getComponent(); UIComponent compositeParent = null; @@ -1122,8 +1126,10 @@ public void broadcast(FacesEvent event) compositeParent.popComponentFromEL(context); } } - setRowIndex(oldRowIndex); + if (currentRowIndex != broadcastedRowIndex) { + setRowIndex(currentRowIndex); + } } /** From 37eaf36748f204021d94d4b272a98f47605c61c4 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 29 Jul 2023 08:43:29 -0400 Subject: [PATCH 07/44] https://github.com/eclipse-ee4j/mojarra/issues/5213 Further improved to only skip index setting when old+new are both -1 --- .../main/java/com/sun/faces/facelets/component/UIRepeat.java | 5 +++-- impl/src/main/java/javax/faces/component/UIData.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) 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 7eb37027bd..3fc96c8d5e 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 @@ -969,11 +969,12 @@ public void broadcast(FacesEvent event) throws AbortProcessingException { this.resetDataModel(ctx); 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(); - if (idx != prevIndex) { + if (needsToSetIndex) { this.setIndex(ctx, idx); } Integer begin = this.getBegin(); @@ -1007,7 +1008,7 @@ public void broadcast(FacesEvent event) throws AbortProcessingException { compositeParent.popComponentFromEL(ctx); } this.updateIterationStatus(ctx, null); - if (idx != prevIndex) { + if (needsToSetIndex) { this.setIndex(ctx, prevIndex); } } diff --git a/impl/src/main/java/javax/faces/component/UIData.java b/impl/src/main/java/javax/faces/component/UIData.java index e32744076e..6c810be48e 100644 --- a/impl/src/main/java/javax/faces/component/UIData.java +++ b/impl/src/main/java/javax/faces/component/UIData.java @@ -1103,8 +1103,9 @@ public void broadcast(FacesEvent event) } int currentRowIndex = getRowIndex(); int broadcastedRowIndex = revent.getRowIndex(); + boolean needsToSetIndex = currentRowIndex != -1 || broadcastedRowIndex != -1; // #5213 - if (currentRowIndex != broadcastedRowIndex) { + if (needsToSetIndex) { setRowIndex(broadcastedRowIndex); } @@ -1127,7 +1128,7 @@ public void broadcast(FacesEvent event) } } - if (currentRowIndex != broadcastedRowIndex) { + if (needsToSetIndex) { setRowIndex(currentRowIndex); } } From e0cd6ecd7d328837e28b14bc62c4f60e8a64eaff Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 29 Jul 2023 08:57:25 -0400 Subject: [PATCH 08/44] https://github.com/eclipse-ee4j/mojarra/issues/5291 jakarta.faces.NUMBER_OF_CLIENT_WINDOWS was not considered as context param --- .../ClientWindowScopeContextManager.java | 40 ++++++++++++------- .../sun/faces/config/WebConfiguration.java | 1 + 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/cdi/clientwindow/ClientWindowScopeContextManager.java b/impl/src/main/java/com/sun/faces/cdi/clientwindow/ClientWindowScopeContextManager.java index 7c448c1c00..5a2a9d4c90 100644 --- a/impl/src/main/java/com/sun/faces/cdi/clientwindow/ClientWindowScopeContextManager.java +++ b/impl/src/main/java/com/sun/faces/cdi/clientwindow/ClientWindowScopeContextManager.java @@ -17,26 +17,28 @@ package com.sun.faces.cdi.clientwindow; +import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableDistributable; +import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.NumberOfClientWindows; +import static java.util.logging.Level.FINEST; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + import com.sun.faces.config.WebConfiguration; import com.sun.faces.util.FacesLogger; import com.sun.faces.util.LRUMap; + import jakarta.enterprise.context.spi.Contextual; import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.inject.spi.PassivationCapable; import jakarta.faces.context.ExternalContext; import jakarta.faces.context.FacesContext; -import jakarta.faces.lifecycle.ClientWindow; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSessionEvent; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - -import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableDistributable; -import static java.util.logging.Level.FINEST; - /** * The manager that deals with CDI ClientWindowScoped beans. */ @@ -141,12 +143,22 @@ private Map getContextMap(FacesContext f String clientWindowId = getCurrentClientWindowId(facesContext); if (clientWindowScopeContexts == null && create) { - synchronized (session) { - Integer size = (Integer) sessionMap.get(ClientWindow.NUMBER_OF_CLIENT_WINDOWS_PARAM_NAME); - if (size == null) { - size = 10; + Integer numberOfClientWindows = null; + + try { + numberOfClientWindows = Integer.parseInt(WebConfiguration.getInstance(externalContext).getOptionValue(NumberOfClientWindows)); + } catch (NumberFormatException nfe) { + if (LOGGER.isLoggable(Level.WARNING)) { + LOGGER.log(Level.WARNING, "Unable to set number of client windows. Defaulting to {0}", NumberOfClientWindows.getDefaultValue()); } - sessionMap.put(CLIENT_WINDOW_CONTEXTS, Collections.synchronizedMap(new LRUMap(size))); + } + + if (numberOfClientWindows == null) { + numberOfClientWindows = Integer.valueOf(NumberOfClientWindows.getDefaultValue()); + } + + synchronized (session) { + sessionMap.put(CLIENT_WINDOW_CONTEXTS, Collections.synchronizedMap(new LRUMap(numberOfClientWindows))); } } diff --git a/impl/src/main/java/com/sun/faces/config/WebConfiguration.java b/impl/src/main/java/com/sun/faces/config/WebConfiguration.java index f66dd7d28a..da43ad076e 100644 --- a/impl/src/main/java/com/sun/faces/config/WebConfiguration.java +++ b/impl/src/main/java/com/sun/faces/config/WebConfiguration.java @@ -756,6 +756,7 @@ public enum WebContextInitParameter { JakartaFacesProjectStage(ProjectStage.PROJECT_STAGE_PARAM_NAME, "Production"), AlternateLifecycleId(FacesServlet.LIFECYCLE_ID_ATTR, ""), ResourceExcludes(ResourceHandler.RESOURCE_EXCLUDES_PARAM_NAME, ResourceHandler.RESOURCE_EXCLUDES_DEFAULT_VALUE), + NumberOfClientWindows(ClientWindow.NUMBER_OF_CLIENT_WINDOWS_PARAM_NAME, "10"), NumberOfViews("com.sun.faces.numberOfViewsInSession", "15"), NumberOfLogicalViews("com.sun.faces.numberOfLogicalViews", "15"), NumberOfActiveViewMaps("com.sun.faces.numberOfActiveViewMaps", "25"), From 1f6939988743b1f04688b54111d18a03423d4d70 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 30 Jul 2023 10:23:44 -0400 Subject: [PATCH 09/44] https://github.com/jakartaee/faces/issues/1567 Improved impl of "retargeted ajax behavior" --- .../tag/composite/RetargetedAjaxBehavior.java | 229 ++++++++++++++++++ .../facelets/tag/jsf/core/AjaxHandler.java | 2 + .../html_basic/AjaxBehaviorRenderer.java | 21 +- 3 files changed, 237 insertions(+), 15 deletions(-) create mode 100644 impl/src/main/java/com/sun/faces/facelets/tag/composite/RetargetedAjaxBehavior.java 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..e33860c579 --- /dev/null +++ b/impl/src/main/java/com/sun/faces/facelets/tag/composite/RetargetedAjaxBehavior.java @@ -0,0 +1,229 @@ +/* + * 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 javax.el.ValueExpression; +import javax.faces.component.UIComponent; +import javax.faces.component.behavior.AjaxBehavior; +import javax.faces.component.behavior.ClientBehaviorContext; +import javax.faces.component.behavior.ClientBehaviorHint; +import javax.faces.context.FacesContext; +import javax.faces.event.AbortProcessingException; +import javax.faces.event.AjaxBehaviorListener; +import javax.faces.event.BehaviorEvent; + +/** + * Basically represents which is retargeted by in AjaxHandler and checked in AjaxBehaviorRenderer. + * + * @see https://github.com/jakartaee/faces/issues/1567 + */ +public class RetargetedAjaxBehavior extends AjaxBehavior { + + private AjaxBehavior retargeted; + + public RetargetedAjaxBehavior(AjaxBehavior retargeted) { + this.retargeted = retargeted; + } + + @Override + public String getScript(ClientBehaviorContext behaviorContext) { + return retargeted.getScript(behaviorContext); + } + + @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/jsf/core/AjaxHandler.java b/impl/src/main/java/com/sun/faces/facelets/tag/jsf/core/AjaxHandler.java index ba056209fe..7c77b02ea4 100644 --- a/impl/src/main/java/com/sun/faces/facelets/tag/jsf/core/AjaxHandler.java +++ b/impl/src/main/java/com/sun/faces/facelets/tag/jsf/core/AjaxHandler.java @@ -56,6 +56,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.jsf.CompositeComponentTagHandler; import com.sun.faces.renderkit.RenderKitUtils; @@ -345,6 +346,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/renderkit/html_basic/AjaxBehaviorRenderer.java b/impl/src/main/java/com/sun/faces/renderkit/html_basic/AjaxBehaviorRenderer.java index 92e0a96e0c..d54067e66c 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 @@ -43,7 +43,7 @@ import javax.faces.event.PhaseId; import javax.faces.render.ClientBehaviorRenderer; -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; @@ -238,9 +238,9 @@ private static String buildAjaxCommand(ClientBehaviorContext behaviorContext, 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()) { @@ -303,6 +303,7 @@ private static String buildAjaxCommand(ClientBehaviorContext behaviorContext, // Appends an ids argument to the ajax command private static void appendIds(FacesContext facesContext, UIComponent component, + AjaxBehavior ajaxBehavior, StringBuilder builder, Collection ids) { @@ -318,7 +319,7 @@ private static void appendIds(FacesContext facesContext, boolean first = true; - UIComponent composite = UIComponent.getCompositeComponentParent(component); + UIComponent composite = (ajaxBehavior instanceof RetargetedAjaxBehavior) ? UIComponent.getCompositeComponentParent(component) : null; String separatorChar = String.valueOf(getSeparatorChar(facesContext)); for (String id : ids) { @@ -335,7 +336,7 @@ private static void appendIds(FacesContext facesContext, 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 && (expression.equals("@this") || expression.startsWith("@this" + separatorChar))) { expression = expression.replaceFirst("@this", separatorChar + composite.getClientId(facesContext)); clientResolveableExpression = false; } @@ -372,16 +373,6 @@ private static void appendIds(FacesContext facesContext, 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) { From db8b436c9f393995fa8dd15c0e91cd7d7fa17f57 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 30 Jul 2023 10:27:02 -0400 Subject: [PATCH 10/44] Improved javadoc comment --- .../faces/facelets/tag/composite/RetargetedAjaxBehavior.java | 2 ++ 1 file changed, 2 insertions(+) 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 index e33860c579..b8a4a76d02 100644 --- 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 @@ -31,6 +31,8 @@ /** * Basically represents which is retargeted by in AjaxHandler and checked in AjaxBehaviorRenderer. + * + * We should probably introduce AjaxBehaviorWrapper in Faces.next to reduce boilerplate like this. * * @see https://github.com/jakartaee/faces/issues/1567 */ From 6a11c61de20c65945c4e2ef1e4497fd9ad7a919b Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 30 Jul 2023 10:35:03 -0400 Subject: [PATCH 11/44] Fixed comment references --- .../sun/faces/facelets/tag/composite/InsertChildrenHandler.java | 2 +- .../faces/facelets/tag/composite/RetargetedAjaxBehavior.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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 3cabe6acc3..62663ef408 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 @@ -108,7 +108,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 index b8a4a76d02..8e05be622b 100644 --- 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 @@ -35,6 +35,7 @@ * We should probably introduce AjaxBehaviorWrapper in Faces.next to reduce boilerplate like this. * * @see https://github.com/jakartaee/faces/issues/1567 + * @see https://github.com/eclipse-ee4j/mojarra/issues/5032 */ public class RetargetedAjaxBehavior extends AjaxBehavior { From ffe1e111f531e1a0ff827f9c86cb2c5f55c2316e Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Thu, 7 Sep 2023 06:21:06 -0400 Subject: [PATCH 12/44] Fix #5308 ambiguous resolution during release of FacesContext when there is more than one implementation of FacesContext known to CDI in bean-discovery-mode=all state --- .../main/java/com/sun/faces/context/FacesContextImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/impl/src/main/java/com/sun/faces/context/FacesContextImpl.java b/impl/src/main/java/com/sun/faces/context/FacesContextImpl.java index d750d15f16..a75013b10d 100644 --- a/impl/src/main/java/com/sun/faces/context/FacesContextImpl.java +++ b/impl/src/main/java/com/sun/faces/context/FacesContextImpl.java @@ -16,6 +16,8 @@ package com.sun.faces.context; +import static java.util.stream.Collectors.toSet; + import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -24,9 +26,11 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import com.sun.faces.cdi.CdiExtension; import com.sun.faces.el.ELContextImpl; import com.sun.faces.renderkit.RenderKitUtils; import com.sun.faces.util.FacesLogger; @@ -556,7 +560,8 @@ public void release() { DEFAULT_FACES_CONTEXT.remove(); // Destroy our instance produced by FacesContextProducer. - Bean bean = beanManager.resolve(beanManager.getBeans(FacesContext.class)); + Set> beans = beanManager.getBeans(FacesContext.class).stream().filter(bean -> CdiExtension.class.isAssignableFrom(bean.getBeanClass())).collect(toSet()); + Bean bean = beanManager.resolve(beans); ((AlterableContext) beanManager.getContext(bean.getScope())).destroy(bean); } From 787ee5b208d1d7b6e3baa5a1da581620995872a3 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Thu, 7 Sep 2023 14:33:47 -0400 Subject: [PATCH 13/44] Fixed merge error during https://github.com/eclipse-ee4j/mojarra/commit/119fa9fbdbe490b7e19d4331f7d414926690f07b --- .../facelets/tag/faces/core/AjaxHandler.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) 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 38a4273415..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 @@ -67,6 +67,7 @@ * values.
The events attribute for this tag that can be a * ValueExpression must be evaluated at tag execution time since the event name is used in the process of * Behavior creation.
If this tag is nested within a single {@link ClientBehaviorHolder} component: + * *
    *
  • If the events attribute value is not specified, obtain the default event name by calling * {@link ClientBehaviorHolder#getDefaultEventName}. If that returns null throw an @@ -77,21 +78,18 @@ *
  • Add the {@link AjaxBehavior} instance to the {@link ClientBehaviorHolder} component by calling * {@link ClientBehaviorHolder#addClientBehavior} passing event and the {@link AjaxBehavior} instance.
  • *
- *
- *
+ *

* Check for the existence of the Ajax resource by calling UIViewRoot.getComponentResources(). If the Ajax * resource does not exist, create a UIOutput component instance and set the renderer type to * jakarta.faces.resource.Script. Set the the following attributes in the component's attribute - * Map: library with the value {@value ResourceHandler#JSF_SCRIPT_LIBRARY_NAME} and - * name with the value {@value ResourceHandler#JSF_SCRIPT_RESOURCE_NAME}. Install the component resource + * Map: library with the value {@value ResourceHandler#FACES_SCRIPT_LIBRARY_NAME} and + * name with the value {@value ResourceHandler#FACES_SCRIPT_RESOURCE_NAME}. Install the component resource * using UIViewRoot.addComponentResource() and specifying head as the target * argument. - *

- * + *

* If this tag has component children, add the {@link AjaxBehavior} to {@link AjaxBehaviors} by calling * {@link AjaxBehaviors#pushBehavior}. As subsequent child components that implement the {@link ClientBehaviorHolder} * interface are evaluated this {@link AjaxBehavior} instance must be added as a behavior to the component. - *

* * @version $Id: AjaxHandler.java 5369 2008-09-08 19:53:45Z rogerk $ */ @@ -304,10 +302,10 @@ private AjaxBehavior createAjaxBehavior(FaceletContext ctx, UIComponent parent, if (parent instanceof BehaviorHolderWrapper) { ValueExpression targets = ((BehaviorHolderWrapper) parent).getTargets(); - + if (targets != null) { String targetClientIds = (String) targets.getValue(ctx); - + if (targetClientIds != null) { Collection executeClientIds = new ArrayList<>(behavior.getExecute()); @@ -324,15 +322,15 @@ private AjaxBehavior createAjaxBehavior(FaceletContext ctx, UIComponent parent, if (null != listener) { behavior.addAjaxBehaviorListener( - new AjaxBehaviorListenerImpl(listener.getMethodExpression(ctx, Object.class, new Class[] { AjaxBehaviorEvent.class }), - listener.getMethodExpression(ctx, Object.class, new Class[] {}))); + new AjaxBehaviorListenerImpl(listener.getMethodExpression(ctx, Object.class, new Class[] { AjaxBehaviorEvent.class }), + listener.getMethodExpression(ctx, Object.class, new Class[] {}))); } return behavior; } // Sets the value from the TagAttribute on the behavior - private void setBehaviorAttribute(FaceletContext ctx, AjaxBehavior behavior, TagAttribute attr, Class type) { + private void setBehaviorAttribute(FaceletContext ctx, AjaxBehavior behavior, TagAttribute attr, Class type) { if (attr != null) { behavior.setValueExpression(attr.getLocalName(), attr.getValueExpression(ctx, type)); From 963d3cf2e644ed37f980cb77cf2182428bc43d86 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Thu, 7 Sep 2023 14:37:59 -0400 Subject: [PATCH 14/44] Fix javadoc errors on #5032 (com.sun.faces.* is also checked in 4.0) --- .../facelets/tag/composite/RetargetedAjaxBehavior.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 index 8e05be622b..2937244c65 100644 --- 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 @@ -30,12 +30,12 @@ import javax.faces.event.BehaviorEvent; /** - * Basically represents which is retargeted by in AjaxHandler and checked in AjaxBehaviorRenderer. + * Basically represents {@code } which is retargeted by {@code } in {@code AjaxHandler} and checked in {@code AjaxBehaviorRenderer}. * - * We should probably introduce AjaxBehaviorWrapper in Faces.next to reduce boilerplate like this. + * We should probably introduce {@code AjaxBehaviorWrapper} in Faces.next to reduce boilerplate like this. * - * @see https://github.com/jakartaee/faces/issues/1567 - * @see https://github.com/eclipse-ee4j/mojarra/issues/5032 + * - https://github.com/jakartaee/faces/issues/1567 + * - https://github.com/eclipse-ee4j/mojarra/issues/5032 */ public class RetargetedAjaxBehavior extends AjaxBehavior { From d7029ed9545bdf809d88868188f58ef3a5c894bb Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Fri, 8 Sep 2023 06:49:44 -0400 Subject: [PATCH 15/44] https://github.com/jakartaee/faces/issues/1567 Fixed regression error which caused spec1567IT to fail; the composite instance is needed further down in ComponentNotFoundException block --- .../sun/faces/renderkit/html_basic/AjaxBehaviorRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 d54067e66c..943547fac7 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 @@ -319,7 +319,7 @@ private static void appendIds(FacesContext facesContext, boolean first = true; - UIComponent composite = (ajaxBehavior instanceof RetargetedAjaxBehavior) ? UIComponent.getCompositeComponentParent(component) : null; + UIComponent composite = UIComponent.getCompositeComponentParent(component); String separatorChar = String.valueOf(getSeparatorChar(facesContext)); for (String id : ids) { @@ -336,7 +336,7 @@ private static void appendIds(FacesContext facesContext, boolean clientResolveableExpression = expression.equals("@all") || expression.equals("@none") || expression.equals("@form") || expression.equals("@this"); - if (composite != null && (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; } From 1d2268cecfc453e7d2a834bcad50cd1e7d28e38f Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Fri, 8 Sep 2023 07:23:37 -0400 Subject: [PATCH 16/44] Fixed regression error which caused 3rd form of spec1567IT to fail; the 'this' instance being passed into getScript() was the wrong one --- .../faces/facelets/tag/composite/RetargetedAjaxBehavior.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 index 2937244c65..980c8b15cf 100644 --- 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 @@ -28,6 +28,7 @@ import javax.faces.event.AbortProcessingException; import javax.faces.event.AjaxBehaviorListener; import javax.faces.event.BehaviorEvent; +import javax.faces.render.ClientBehaviorRenderer; /** * Basically represents {@code } which is retargeted by {@code } in {@code AjaxHandler} and checked in {@code AjaxBehaviorRenderer}. @@ -47,7 +48,8 @@ public RetargetedAjaxBehavior(AjaxBehavior retargeted) { @Override public String getScript(ClientBehaviorContext behaviorContext) { - return retargeted.getScript(behaviorContext); + ClientBehaviorRenderer renderer = getRenderer(behaviorContext.getFacesContext()); + return renderer != null ? renderer.getScript(behaviorContext, this) : null; } @Override From 5256df0e6f53d886427d8efc28e73dabb62b46fe Mon Sep 17 00:00:00 2001 From: Eclipse Mojarra Bot Date: Fri, 8 Sep 2023 14:45:18 +0000 Subject: [PATCH 17/44] Prepare release org.glassfish:mojarra-parent:4.0.4 --- action/pom.xml | 2 +- cdi/pom.xml | 2 +- impl/pom.xml | 2 +- pom.xml | 2 +- rest/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/action/pom.xml b/action/pom.xml index f5fd8afa1b..e26fedbc01 100644 --- a/action/pom.xml +++ b/action/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.4-SNAPSHOT + 4.0.4 org.eclipse.mojarra diff --git a/cdi/pom.xml b/cdi/pom.xml index f2d602fcbf..58be26230a 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.4-SNAPSHOT + 4.0.4 org.eclipse.mojarra diff --git a/impl/pom.xml b/impl/pom.xml index 6fdeed619a..39f631bf10 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -29,7 +29,7 @@ org.glassfish mojarra-parent - 4.0.4-SNAPSHOT + 4.0.4 jakarta.faces diff --git a/pom.xml b/pom.xml index 7e5bf323ec..3b1e305b75 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.glassfish mojarra-parent - 4.0.4-SNAPSHOT + 4.0.4 pom Mojarra ${project.version} - Project diff --git a/rest/pom.xml b/rest/pom.xml index 1cdd181430..ae79c04c0b 100644 --- a/rest/pom.xml +++ b/rest/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.4-SNAPSHOT + 4.0.4 org.eclipse.mojarra From 606b3c7da9af96d26565d288ec8d5287d8ce4206 Mon Sep 17 00:00:00 2001 From: Eclipse Mojarra Bot Date: Fri, 8 Sep 2023 14:47:37 +0000 Subject: [PATCH 18/44] Prepare next development cycle for 4.0.5-SNAPSHOT --- action/pom.xml | 2 +- cdi/pom.xml | 2 +- impl/pom.xml | 2 +- pom.xml | 2 +- rest/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/action/pom.xml b/action/pom.xml index e26fedbc01..0739f3729e 100644 --- a/action/pom.xml +++ b/action/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.4 + 4.0.5-SNAPSHOT org.eclipse.mojarra diff --git a/cdi/pom.xml b/cdi/pom.xml index 58be26230a..9726b77270 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.4 + 4.0.5-SNAPSHOT org.eclipse.mojarra diff --git a/impl/pom.xml b/impl/pom.xml index 39f631bf10..49bf07d410 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -29,7 +29,7 @@ org.glassfish mojarra-parent - 4.0.4 + 4.0.5-SNAPSHOT jakarta.faces diff --git a/pom.xml b/pom.xml index 3b1e305b75..7ce609e63f 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.glassfish mojarra-parent - 4.0.4 + 4.0.5-SNAPSHOT pom Mojarra ${project.version} - Project diff --git a/rest/pom.xml b/rest/pom.xml index ae79c04c0b..7dcc5989a1 100644 --- a/rest/pom.xml +++ b/rest/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.4 + 4.0.5-SNAPSHOT org.eclipse.mojarra From e21dd15ac1bb12e1fa344e2aeb57165cf162218d Mon Sep 17 00:00:00 2001 From: Eclipse Mojarra Bot Date: Fri, 8 Sep 2023 15:00:08 +0000 Subject: [PATCH 19/44] Prepare release org.glassfish:mojarra-parent:2.3.21 --- impl/pom.xml | 2 +- pom.xml | 2 +- test/pom.xml | 2 +- util/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/impl/pom.xml b/impl/pom.xml index 827c05871b..35deebf9f1 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -27,7 +27,7 @@ org.glassfish mojarra-parent - 2.3.21-SNAPSHOT + 2.3.21 jakarta.faces diff --git a/pom.xml b/pom.xml index c6fff1635f..bc3a61b04a 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ org.glassfish mojarra-parent - 2.3.21-SNAPSHOT + 2.3.21 pom Mojarra ${project.version} - Project diff --git a/test/pom.xml b/test/pom.xml index 2b3051d2ce..8bc8dc1f17 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -193,7 +193,7 @@ com.sun.faces util - 2.3.21-SNAPSHOT + 2.3.21 test diff --git a/util/pom.xml b/util/pom.xml index ed3fe7a2ef..653da890bc 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -24,7 +24,7 @@ org.glassfish mojarra-parent - 2.3.21-SNAPSHOT + 2.3.21 com.sun.faces From f4b475f38e63b0fec2a60706e27d6cb082f912b0 Mon Sep 17 00:00:00 2001 From: Eclipse Mojarra Bot Date: Fri, 8 Sep 2023 15:02:15 +0000 Subject: [PATCH 20/44] Prepare next development cycle for 2.3.22-SNAPSHOT --- impl/pom.xml | 2 +- pom.xml | 2 +- test/pom.xml | 2 +- util/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/impl/pom.xml b/impl/pom.xml index 35deebf9f1..282f43f929 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -27,7 +27,7 @@ org.glassfish mojarra-parent - 2.3.21 + 2.3.22-SNAPSHOT jakarta.faces diff --git a/pom.xml b/pom.xml index bc3a61b04a..8370769588 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ org.glassfish mojarra-parent - 2.3.21 + 2.3.22-SNAPSHOT pom Mojarra ${project.version} - Project diff --git a/test/pom.xml b/test/pom.xml index 8bc8dc1f17..5d6fd2834d 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -193,7 +193,7 @@ com.sun.faces util - 2.3.21 + 2.3.22-SNAPSHOT test diff --git a/util/pom.xml b/util/pom.xml index 653da890bc..f494ccabf3 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -24,7 +24,7 @@ org.glassfish mojarra-parent - 2.3.21 + 2.3.22-SNAPSHOT com.sun.faces From 1f272e5e6842f21137a37a7730fef0f2889ec160 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 9 Sep 2023 11:54:19 -0400 Subject: [PATCH 21/44] Composite component can't find reparented child when it's in turn in another NamingContainer within the composite such as h:form or h:dataTable; upon code analysis I found a line which seemed completely unnecessary so I have removed it and it works again for me. https://github.com/eclipse-ee4j/mojarra/issues/5214 --- .../facelets/tag/faces/ComponentTagHandlerDelegateImpl.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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; } From 6e8224e8daf0af16e4a682d9d85aebd6f18dae4e Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 30 Sep 2023 11:01:23 -0400 Subject: [PATCH 22/44] Move back weld reinit from ConfigureListener into FacesInitializer https://github.com/eclipse-ee4j/mojarra/issues/5321 --- .../sun/faces/config/FacesInitializer.java | 43 +++++++++++++++++++ .../main/java/com/sun/faces/util/Util.java | 15 ------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/config/FacesInitializer.java b/impl/src/main/java/com/sun/faces/config/FacesInitializer.java index e56a2bc78d..c01c0aa277 100644 --- a/impl/src/main/java/com/sun/faces/config/FacesInitializer.java +++ b/impl/src/main/java/com/sun/faces/config/FacesInitializer.java @@ -22,11 +22,16 @@ import static com.sun.faces.RIConstants.FACES_SERVLET_REGISTRATION; import static com.sun.faces.util.Util.isEmpty; import static java.lang.Boolean.parseBoolean; +import static java.util.logging.Level.WARNING; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.logging.Logger; +import com.sun.faces.util.FacesLogger; + +import jakarta.enterprise.inject.spi.BeanManager; import jakarta.faces.annotation.FacesConfig; import jakarta.faces.application.ResourceDependencies; import jakarta.faces.application.ResourceDependency; @@ -84,6 +89,8 @@ public class FacesInitializer implements ServletContainerInitializer { // NOTE: Logging should not be used with this class. + // NOTE: It can, we only need to ensure that the logger is only invoked when there's a (Init)FacesContext. + private static final Logger LOGGER = FacesLogger.CONFIG.getLogger(); public static final String FACES_PACKAGE_PREFIX = "jakarta.faces."; public static final String MOJARRA_PACKAGE_PREFIX = "com.sun.faces."; @@ -112,6 +119,7 @@ public void onStartup(Set> classes, ServletContext servletContext) thro } // Other concerns also handled if there is an existing Faces Servlet mapping + handleCdiConcerns(servletContext); handleWebSocketConcerns(servletContext); // The Configure listener will do the bulk of initializing (configuring) Faces in a later phase. @@ -204,6 +212,41 @@ private static void handleMappingConcerns(ServletContext servletContext) throws servletContext.setAttribute(FACES_SERVLET_REGISTRATION, newFacesServletRegistration); } + private static void handleCdiConcerns(ServletContext context) throws ServletException { + // If Weld is used as CDI impl then make sure it doesn't skip initialization when there's no BDA. See also #5232 and #5321 + + if (context.getAttribute("org.jboss.weld.environment.servlet.jakarta.enterprise.inject.spi.BeanManager") instanceof BeanManager) { + // Already initialized. + return; + } + + ClassLoader cl = context.getClassLoader(); + Class weldInitializerClass; + + try { + weldInitializerClass = cl.loadClass("org.jboss.weld.environment.servlet.EnhancedListener"); + } + catch (ClassNotFoundException ignore) { + // Weld is actually not being used. That's OK for now, so just continue as usual. + return; + } + + // Weld is being used so let's make sure it doesn't skip initialization when there's no BDA. + context.setInitParameter("org.jboss.weld.environment.servlet.archive.isolation", "false"); + + if (context.getAttribute("org.jboss.weld.environment.servlet.enhancedListenerUsed") == Boolean.TRUE) { + try { + LOGGER.log(WARNING, "Weld skipped initialization - forcing it to reinitialize"); + ServletContainerInitializer weldInitializer = (ServletContainerInitializer) weldInitializerClass.getConstructor().newInstance(); + weldInitializer.onStartup(null, context); + } + catch (Exception | LinkageError e) { + // Weld is being used but it failed for unclear reason while CDI should be enabled. That's not OK, so rethrow as ServletException. + throw new ServletException("Reinitializing Weld failed - giving up, please make sure your project contains at least one bean class with a bean defining annotation and retry", e); + } + } + } + private static void handleWebSocketConcerns(ServletContext context) throws ServletException { if (context.getAttribute(ServerContainer.class.getName()) != null) { // Already initialized 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 48e26e1d96..14c62e2db7 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -30,7 +30,6 @@ import static java.util.Collections.emptyList; import static java.util.logging.Level.FINE; import static java.util.logging.Level.SEVERE; -import static java.util.logging.Level.WARNING; import java.beans.FeatureDescriptor; import java.io.IOException; @@ -101,7 +100,6 @@ import jakarta.faces.event.AbortProcessingException; import jakarta.faces.render.ResponseStateManager; import jakarta.faces.webapp.FacesServlet; -import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRegistration; import jakarta.servlet.http.HttpServletMapping; @@ -1476,19 +1474,6 @@ public static BeanManager getCdiBeanManager(FacesContext facesContext) { if (result == null && facesContext != null) { Map applicationMap = facesContext.getExternalContext().getApplicationMap(); result = (BeanManager) applicationMap.get("org.jboss.weld.environment.servlet.jakarta.enterprise.inject.spi.BeanManager"); - - if (result == null && applicationMap.get("org.jboss.weld.environment.servlet.enhancedListenerUsed") == Boolean.TRUE) { - LOGGER.log(WARNING, "Weld skipped initialization - forcing it to reinitialize"); - try { - ServletContext servletContext = (ServletContext) facesContext.getExternalContext().getContext(); - servletContext.setInitParameter("org.jboss.weld.environment.servlet.archive.isolation", "false"); - ServletContainerInitializer weld = (ServletContainerInitializer) Class.forName("org.jboss.weld.environment.servlet.EnhancedListener").getConstructor().newInstance(); - weld.onStartup(null, servletContext); - result = (BeanManager) applicationMap.get("org.jboss.weld.environment.servlet.jakarta.enterprise.inject.spi.BeanManager"); - } catch (Exception | LinkageError e) { - LOGGER.log(WARNING, "Reinitializing Weld failed - giving up, please make sure your project contains at least one bean class with a bean defining annotation and retry", e); - } - } } if (result != null && facesContext != null) { From 08997dea23d86e499fb9748c0cb365c1f867889e Mon Sep 17 00:00:00 2001 From: Manfred Riem Date: Sun, 1 Oct 2023 08:07:55 -0500 Subject: [PATCH 23/44] Fixes #5316 - ClientWindow: Custom PrimeClientWindowFactory not being respected (#5323) Co-authored-by: Manfred Riem --- .../sun/faces/lifecycle/LifecycleImpl.java | 28 ------------------- 1 file changed, 28 deletions(-) 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")); } From ac296492a5f406f34cf8a699e0ed53b106c50e7a Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 8 Oct 2023 12:04:07 -0400 Subject: [PATCH 24/44] Refactor retrieval and validation of webapp contracts directory into a single place and fixed a couple of misassumptions about it always being an absolute path https://github.com/eclipse-ee4j/mojarra/issues/5329 --- .../application/ApplicationAssociate.java | 11 ++++++---- .../resource/FaceletWebappResourceHelper.java | 13 +++++------ .../application/resource/ResourceManager.java | 19 +++++++++++++--- .../sun/faces/config/WebConfiguration.java | 4 ++-- .../faces/facelets/impl/DefaultFacelet.java | 21 ++++++++++-------- .../facelets/impl/DefaultFaceletFactory.java | 22 +++++++++++++++++++ .../facelets/tag/ui/CompositionHandler.java | 7 +----- .../facelets/tag/ui/DecorateHandler.java | 7 +----- .../faces/facelets/tag/ui/IncludeHandler.java | 7 +----- .../sun/faces/renderkit/RenderKitUtils.java | 6 ++--- .../html_basic/ScriptStyleBaseRenderer.java | 6 ++--- 11 files changed, 73 insertions(+), 50 deletions(-) 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 8b9f6b1440..9ac13faae4 100644 --- a/impl/src/main/java/com/sun/faces/application/ApplicationAssociate.java +++ b/impl/src/main/java/com/sun/faces/application/ApplicationAssociate.java @@ -201,12 +201,15 @@ 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()); + return ApplicationAssociate.getInstance(facesContext.getExternalContext()); } public static ApplicationAssociate getInstance(ExternalContext externalContext) { 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..2d5508650e 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; @@ -48,12 +47,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, " "); } @@ -150,9 +149,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 +225,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 2a491fa2b8..2d3b3ea631 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,12 +59,15 @@ public class ResourceManager { */ private static final Pattern CONFIG_MIMETYPE_PATTERN = Pattern.compile("[a-z-]*/[a-z0-9.\\*-]*"); - private FaceletWebappResourceHelper faceletWebappResourceHelper = new FaceletWebappResourceHelper(); - /** * {@link ResourceHelper} used for looking up webapp-based resources. */ - private 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. @@ -175,6 +180,14 @@ public ResourceInfo findResource(String libraryName, String resourceName, String public Stream getViewResources(FacesContext facesContext, String path, int maxDepth, ResourceVisitOption... options) { 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 diff --git a/impl/src/main/java/com/sun/faces/config/WebConfiguration.java b/impl/src/main/java/com/sun/faces/config/WebConfiguration.java index da43ad076e..7b72496284 100644 --- a/impl/src/main/java/com/sun/faces/config/WebConfiguration.java +++ b/impl/src/main/java/com/sun/faces/config/WebConfiguration.java @@ -396,7 +396,8 @@ private void discoverResourceLibraryContracts() { Set candidates; // Scan for "contractMappings" in the web app root - String contractsDirName = getOptionValue(WebContextInitParameter.WebAppContractsDirectory); + ApplicationAssociate associate = ApplicationAssociate.getCurrentInstance(); + String contractsDirName = associate.getResourceManager().getBaseContractsPath(); assert null != contractsDirName; candidates = extContex.getResourcePaths(contractsDirName); if (null != candidates) { @@ -437,7 +438,6 @@ private void discoverResourceLibraryContracts() { Map> contractMappings = new HashMap<>(); - ApplicationAssociate associate = ApplicationAssociate.getCurrentInstance(); Map> contractsFromConfig = associate.getResourceLibraryContracts(); List contractsToExpose; 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/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/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/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)); From 9f72851c7602fc2e6d7afeb7547fde1327bc4338 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 8 Oct 2023 12:07:40 -0400 Subject: [PATCH 25/44] Fixed creepy whitespace after self-review --- .../com/sun/faces/application/ApplicationAssociate.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 9ac13faae4..36d61b5bcb 100644 --- a/impl/src/main/java/com/sun/faces/application/ApplicationAssociate.java +++ b/impl/src/main/java/com/sun/faces/application/ApplicationAssociate.java @@ -201,15 +201,15 @@ public static ApplicationAssociate getCurrentInstance() { } public static ApplicationAssociate getInstance() { - return getInstance(FacesContext.getCurrentInstance()); + return getInstance(FacesContext.getCurrentInstance()); } public static ApplicationAssociate getInstance(FacesContext facesContext) { if (facesContext == null) { return null; - } + } - return ApplicationAssociate.getInstance(facesContext.getExternalContext()); + return ApplicationAssociate.getInstance(facesContext.getExternalContext()); } public static ApplicationAssociate getInstance(ExternalContext externalContext) { From f45d5ae8218aec3ba7985b4d1b93f523110b6797 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 14 Oct 2023 14:09:59 -0400 Subject: [PATCH 26/44] Remove unused constants for com.sun.faces.* context params https://github.com/eclipse-ee4j/mojarra/issues/5331 --- .../main/java/com/sun/faces/config/WebConfiguration.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/config/WebConfiguration.java b/impl/src/main/java/com/sun/faces/config/WebConfiguration.java index 7b72496284..1c4379aa72 100644 --- a/impl/src/main/java/com/sun/faces/config/WebConfiguration.java +++ b/impl/src/main/java/com/sun/faces/config/WebConfiguration.java @@ -749,7 +749,6 @@ public enum WebContextInitParameter { // if a parameter is to be deprecated, then the Deprecated enum element *must* appear after the one that is taking // its place. The reporting logic depends on this. - ManagedBeanFactoryDecorator("com.sun.faces.managedBeanFactoryDecoratorClass", ""), StateSavingMethod(StateManager.STATE_SAVING_METHOD_PARAM_NAME, "server"), FaceletsSuffix(ViewHandler.FACELETS_SUFFIX_PARAM_NAME, ViewHandler.DEFAULT_FACELETS_SUFFIX), JakartaFacesConfigFiles(FacesServlet.CONFIG_FILES_ATTR, ""), @@ -767,7 +766,6 @@ public enum WebContextInitParameter { FaceletsBufferSize(ViewHandler.FACELETS_BUFFER_SIZE_PARAM_NAME, "1024"), ClientStateWriteBufferSize("com.sun.faces.clientStateWriteBufferSize", "8192"), ResourceBufferSize("com.sun.faces.resourceBufferSize", "2048"), - ExpressionFactory("com.sun.faces.expressionFactory", "com.sun.el.ExpressionFactoryImpl"), ClientStateTimeout("com.sun.faces.clientStateTimeout", ""), DefaultResourceMaxAge("com.sun.faces.defaultResourceMaxAge", "604800000"), // 7 days ResourceUpdateCheckPeriod("com.sun.faces.resourceUpdateCheckPeriod", "5"), // in minutes @@ -859,16 +857,12 @@ public enum BooleanWebContextInitParameter { DisableFacesServletAutomaticMapping(FacesServlet.DISABLE_FACESSERVLET_TO_XHTML_PARAM_NAME, false), AutomaticExtensionlessMapping(FacesServlet.AUTOMATIC_EXTENSIONLESS_MAPPING_PARAM_NAME, false), EnableClientStateDebugging("com.sun.faces.enableClientStateDebugging", false), - EnableHtmlTagLibraryValidator("com.sun.faces.enableHtmlTagLibValidator", false), - EnableCoreTagLibraryValidator("com.sun.faces.enableCoreTagLibValidator", false), PreferXHTMLContentType("com.sun.faces.preferXHTML", false), CompressViewState("com.sun.faces.compressViewState", true), - CompressJavaScript("com.sun.faces.compressJavaScript", true), EnableJSStyleHiding("com.sun.faces.enableJSStyleHiding", false), EnableScriptInAttributeValue("com.sun.faces.enableScriptsInAttributeValues", true), WriteStateAtFormEnd("com.sun.faces.writeStateAtFormEnd", true), EnableLazyBeanValidation("com.sun.faces.enableLazyBeanValidation", true), - EnableLoadBundle11Compatibility("com.sun.faces.enabledLoadBundle11Compatibility", false), SerializeServerState(StateManager.SERIALIZE_SERVER_STATE_PARAM_NAME, false), EnableViewStateIdRendering("com.sun.faces.enableViewStateIdRendering", true), RegisterConverterPropertyEditors("com.sun.faces.registerConverterPropertyEditors", false), @@ -883,9 +877,7 @@ public enum BooleanWebContextInitParameter { EnableThreading("com.sun.faces.enableThreading", false), AllowTextChildren("com.sun.faces.allowTextChildren", false), CacheResourceModificationTimestamp("com.sun.faces.cacheResourceModificationTimestamp", false), - EnableAgressiveSessionDirtying("com.sun.faces.enableAgressiveSessionDirtying", false), EnableDistributable("com.sun.faces.enableDistributable", false), - EnableFaceletsResourceResolverResolveCompositeComponents("com.sun.faces.enableFaceletsResourceResolverCompositeComponents", false), EnableMissingResourceLibraryDetection("com.sun.faces.enableMissingResourceLibraryDetection", false), DisableIdUniquenessCheck("com.sun.faces.disableIdUniquenessCheck", false), EnableTransitionTimeNoOpFlash("com.sun.faces.enableTransitionTimeNoOpFlash", false), From 0ae839a8d0fbe597f28a76227b98db7e63f80caf Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 19 Oct 2023 16:41:05 -0500 Subject: [PATCH 27/44] [MOJARRA_5341] UnsupportedOperationException in InitFacesContext Provide an ExceptionHandler in getExceptionHandler() --- .../main/java/com/sun/faces/config/InitFacesContext.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/impl/src/main/java/com/sun/faces/config/InitFacesContext.java b/impl/src/main/java/com/sun/faces/config/InitFacesContext.java index 96c3312dec..0de180d621 100644 --- a/impl/src/main/java/com/sun/faces/config/InitFacesContext.java +++ b/impl/src/main/java/com/sun/faces/config/InitFacesContext.java @@ -32,6 +32,7 @@ import com.sun.faces.config.initfacescontext.NoOpFacesContext; import com.sun.faces.config.initfacescontext.ServletContextAdapter; import com.sun.faces.context.ApplicationMap; +import com.sun.faces.context.ExceptionHandlerImpl; import com.sun.faces.util.FacesLogger; import com.sun.faces.util.Util; @@ -41,6 +42,7 @@ import jakarta.faces.application.ApplicationFactory; import jakarta.faces.application.ProjectStage; import jakarta.faces.component.UIViewRoot; +import jakarta.faces.context.ExceptionHandler; import jakarta.faces.context.ExternalContext; import jakarta.faces.context.FacesContext; import jakarta.servlet.ServletContext; @@ -109,6 +111,11 @@ public Application getApplication() { return factory.getApplication(); } + @Override + public ExceptionHandler getExceptionHandler() { + return new ExceptionHandlerImpl(false); + } + @Override public boolean isProjectStage(ProjectStage stage) { if (stage == null) { From 14dc7d72e59f5b9b6e758ceb4c3b3e5844afca48 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 21 Oct 2023 09:37:26 -0400 Subject: [PATCH 28/44] Improved impl of #5339 --- .../sun/faces/config/manager/Documents.java | 3 +- .../FacesFlowDefinitionConfigProcessor.java | 3 +- .../main/java/com/sun/faces/util/Util.java | 30 ++++++++++++++++++- .../FacesConfigNamespaceContextTest.java | 3 +- .../faces/FacesConfigOrderingTestCase.java | 11 +++---- 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/config/manager/Documents.java b/impl/src/main/java/com/sun/faces/config/manager/Documents.java index e90fd44df6..218445a90e 100644 --- a/impl/src/main/java/com/sun/faces/config/manager/Documents.java +++ b/impl/src/main/java/com/sun/faces/config/manager/Documents.java @@ -18,6 +18,7 @@ import static com.sun.faces.RIConstants.DOCUMENT_NAMESPACE; import static com.sun.faces.RIConstants.DOCUMENT_VERSION; +import static com.sun.faces.util.Util.createLocalDocumentBuilderFactory; import static com.sun.faces.util.Util.isEmpty; import static java.util.Arrays.asList; import static java.util.logging.Level.INFO; @@ -231,7 +232,7 @@ public static DocumentInfo[] sortDocuments(DocumentInfo[] facesDocuments, FacesC } private static DOMImplementation createDOMImplementation() throws ParserConfigurationException { - DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory documentBuilderFactory = createLocalDocumentBuilderFactory(); documentBuilderFactory.setNamespaceAware(true); return documentBuilderFactory.newDocumentBuilder().getDOMImplementation(); diff --git a/impl/src/main/java/com/sun/faces/config/processor/FacesFlowDefinitionConfigProcessor.java b/impl/src/main/java/com/sun/faces/config/processor/FacesFlowDefinitionConfigProcessor.java index 77da79922c..3ac07b6f51 100644 --- a/impl/src/main/java/com/sun/faces/config/processor/FacesFlowDefinitionConfigProcessor.java +++ b/impl/src/main/java/com/sun/faces/config/processor/FacesFlowDefinitionConfigProcessor.java @@ -16,6 +16,7 @@ package com.sun.faces.config.processor; +import static com.sun.faces.util.Util.createLocalDocumentBuilderFactory; import static com.sun.faces.util.Util.notNull; import java.net.MalformedURLException; @@ -127,7 +128,7 @@ public static Document synthesizeEmptyFlowDefinition(URI uri) throws ParserConfi } String flowName = segments[segments.length - 2]; - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory dbf = createLocalDocumentBuilderFactory(); dbf.setNamespaceAware(true); DocumentBuilder builder = dbf.newDocumentBuilder(); DOMImplementation domImpl = builder.getDOMImplementation(); 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 14c62e2db7..7ae95e534d 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -62,6 +62,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; @@ -269,12 +270,27 @@ public static boolean isUnitTestModeEnabled() { return unitTestModeEnabled; } + public static interface ThrowingBiConsumer { + void accept(T t, U u) throws Exception; + } + + private static void setPossiblyUnsupportedFeature(ThrowingBiConsumer setter, F feature, Boolean flag) { + try { + setter.accept(feature, flag); + } catch (Exception e) { + throw new IllegalStateException("The feature '" + feature + "' is not supported by your XML processor.", 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, ""); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); + setPossiblyUnsupportedFeature(factory::setFeature, XMLConstants.FEATURE_SECURE_PROCESSING, true); } finally { Thread.currentThread().setContextClassLoader(cl); } @@ -298,13 +314,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(); + 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); + setPossiblyUnsupportedFeature(factory::setFeature, "http://xml.org/sax/features/external-general-entities", false); + factory.setXIncludeAware(false); + factory.setExpandEntityReferences(false); + setPossiblyUnsupportedFeature(factory::setFeature, XMLConstants.FEATURE_SECURE_PROCESSING, true); + return factory; + } + public static SchemaFactory createSchemaFactory(String uri) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); SchemaFactory factory; 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(); From 886f1bcae9315e67f7eefa39a6125aef1040e519 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 22 Oct 2023 10:17:51 -0400 Subject: [PATCH 29/44] Further improved impl of #5339 --- .../main/java/com/sun/faces/util/Util.java | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) 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 7ae95e534d..57e2863a38 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -21,6 +21,7 @@ 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; @@ -274,11 +275,19 @@ public static interface ThrowingBiConsumer { void accept(T t, U u) throws Exception; } - private static void setPossiblyUnsupportedFeature(ThrowingBiConsumer setter, F feature, Boolean flag) { + private static void setFeature(ThrowingBiConsumer setter, F feature, Boolean flag) { try { setter.accept(feature, flag); } catch (Exception e) { - throw new IllegalStateException("The feature '" + feature + "' is not supported by your XML processor.", 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); } } @@ -288,9 +297,9 @@ public static TransformerFactory createTransformerFactory() { try { Thread.currentThread().setContextClassLoader(Util.class.getClassLoader()); factory = TransformerFactory.newInstance(); - factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); - factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); - setPossiblyUnsupportedFeature(factory::setFeature, XMLConstants.FEATURE_SECURE_PROCESSING, true); + 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); } @@ -324,12 +333,12 @@ public static DocumentBuilderFactory createDocumentBuilderFactory() { public static DocumentBuilderFactory createLocalDocumentBuilderFactory() { DocumentBuilderFactory factory; factory = DocumentBuilderFactory.newInstance(); - 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); - setPossiblyUnsupportedFeature(factory::setFeature, "http://xml.org/sax/features/external-general-entities", false); factory.setXIncludeAware(false); factory.setExpandEntityReferences(false); - setPossiblyUnsupportedFeature(factory::setFeature, XMLConstants.FEATURE_SECURE_PROCESSING, true); + 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; } From 7f3e5227fbc83f319d8c28ffe3eb17ebcc78f0a8 Mon Sep 17 00:00:00 2001 From: Paul Ferraro Date: Tue, 31 Oct 2023 18:33:34 -0400 Subject: [PATCH 30/44] [5345] Do not override application-specified "com.sun.faces.enableDistributable" value. --- .../main/java/com/sun/faces/config/ConfigureListener.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/config/ConfigureListener.java b/impl/src/main/java/com/sun/faces/config/ConfigureListener.java index 684baa88bb..7775923b4c 100644 --- a/impl/src/main/java/com/sun/faces/config/ConfigureListener.java +++ b/impl/src/main/java/com/sun/faces/config/ConfigureListener.java @@ -150,8 +150,11 @@ public void contextInitialized(ServletContextEvent servletContextEvent) { } } - if (webXmlProcessor.isDistributablePresent()) { - webConfig.setOptionEnabled(WebConfiguration.BooleanWebContextInitParameter.EnableDistributable, true); + // Do not override if already defined + if (!webConfig.isSet(WebConfiguration.BooleanWebContextInitParameter.EnableDistributable)) { + webConfig.setOptionEnabled(WebConfiguration.BooleanWebContextInitParameter.EnableDistributable, webXmlProcessor.isDistributablePresent()); + } + if (webConfig.isOptionEnabled(WebConfiguration.BooleanWebContextInitParameter.EnableDistributable)) { servletContext.setAttribute(WebConfiguration.BooleanWebContextInitParameter.EnableDistributable.getQualifiedName(), TRUE); } From fbeee0c0747770884314419686cbbee0ebb9e87a Mon Sep 17 00:00:00 2001 From: Eclipse Mojarra Bot Date: Wed, 1 Nov 2023 15:07:47 +0000 Subject: [PATCH 31/44] Prepare release org.glassfish:mojarra-parent:4.0.5 --- action/pom.xml | 2 +- cdi/pom.xml | 2 +- impl/pom.xml | 2 +- pom.xml | 2 +- rest/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/action/pom.xml b/action/pom.xml index 0739f3729e..7e88ebfc4e 100644 --- a/action/pom.xml +++ b/action/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.5-SNAPSHOT + 4.0.5 org.eclipse.mojarra diff --git a/cdi/pom.xml b/cdi/pom.xml index 9726b77270..11ba603330 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.5-SNAPSHOT + 4.0.5 org.eclipse.mojarra diff --git a/impl/pom.xml b/impl/pom.xml index 49bf07d410..c9fae9cfcd 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -29,7 +29,7 @@ org.glassfish mojarra-parent - 4.0.5-SNAPSHOT + 4.0.5 jakarta.faces diff --git a/pom.xml b/pom.xml index 7ce609e63f..58d15df541 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.glassfish mojarra-parent - 4.0.5-SNAPSHOT + 4.0.5 pom Mojarra ${project.version} - Project diff --git a/rest/pom.xml b/rest/pom.xml index 7dcc5989a1..4e21003c14 100644 --- a/rest/pom.xml +++ b/rest/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.5-SNAPSHOT + 4.0.5 org.eclipse.mojarra From ab8cbc1e604bdd25e4988c999aafc4f2149443c2 Mon Sep 17 00:00:00 2001 From: Eclipse Mojarra Bot Date: Wed, 1 Nov 2023 15:10:47 +0000 Subject: [PATCH 32/44] Prepare next development cycle for 4.0.6-SNAPSHOT --- action/pom.xml | 2 +- cdi/pom.xml | 2 +- impl/pom.xml | 2 +- pom.xml | 2 +- rest/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/action/pom.xml b/action/pom.xml index 7e88ebfc4e..74c546d04b 100644 --- a/action/pom.xml +++ b/action/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.5 + 4.0.6-SNAPSHOT org.eclipse.mojarra diff --git a/cdi/pom.xml b/cdi/pom.xml index 11ba603330..ba689d85a4 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.5 + 4.0.6-SNAPSHOT org.eclipse.mojarra diff --git a/impl/pom.xml b/impl/pom.xml index c9fae9cfcd..9f0d5182fb 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -29,7 +29,7 @@ org.glassfish mojarra-parent - 4.0.5 + 4.0.6-SNAPSHOT jakarta.faces diff --git a/pom.xml b/pom.xml index 58d15df541..29057a36ba 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.glassfish mojarra-parent - 4.0.5 + 4.0.6-SNAPSHOT pom Mojarra ${project.version} - Project diff --git a/rest/pom.xml b/rest/pom.xml index 4e21003c14..18d61ee230 100644 --- a/rest/pom.xml +++ b/rest/pom.xml @@ -22,7 +22,7 @@ org.glassfish mojarra-parent - 4.0.5 + 4.0.6-SNAPSHOT org.eclipse.mojarra From 460e797f730928150745ab1d562e9fbcffd87530 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 26 Nov 2023 11:48:31 -0400 Subject: [PATCH 33/44] Make sure that ViewHandler#getViews() also returns programmatic facelets https://github.com/eclipse-ee4j/mojarra/issues/5362 --- .../resource/FaceletWebappResourceHelper.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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 2d5508650e..a79c490b26 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 @@ -31,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; @@ -113,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() // The value is not @Nonbinding. Only in 4.1 we can use View.Literal.INSTANCE as qualifier. + .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) { From 377c749c7747084e48f230900ff4b7bca123e1c7 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 26 Nov 2023 14:39:17 -0400 Subject: [PATCH 34/44] Remove jakarta.faces.visit.SKIP_ITERATION https://github.com/eclipse-ee4j/mojarra/issues/5355 --- .../FaceletFullStateManagementStrategy.java | 141 ++++++++---------- ...FaceletPartialStateManagementStrategy.java | 110 +++++++------- .../application/view/FormOmittedChecker.java | 31 ++-- .../sun/faces/lifecycle/RestoreViewPhase.java | 18 +-- 4 files changed, 128 insertions(+), 172 deletions(-) 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 96b7b8be92..4b50125348 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/FormOmittedChecker.java b/impl/src/main/java/com/sun/faces/application/view/FormOmittedChecker.java index 1375e70e9d..2463b52c39 100644 --- a/impl/src/main/java/com/sun/faces/application/view/FormOmittedChecker.java +++ b/impl/src/main/java/com/sun/faces/application/view/FormOmittedChecker.java @@ -16,6 +16,8 @@ package com.sun.faces.application.view; +import static jakarta.faces.component.visit.VisitHint.SKIP_ITERATION; + import java.util.EnumSet; import java.util.List; import java.util.Set; @@ -42,7 +44,7 @@ class FormOmittedChecker { /** * Stores the skip hint. */ - private static String SKIP_ITERATION_HINT = "jakarta.faces.visit.SKIP_ITERATION"; + private static final Set SKIP_ITERATION_HINT = EnumSet.of(SKIP_ITERATION); /** * Constructor. @@ -61,24 +63,17 @@ public static void check(FacesContext context) { List children = viewRoot.getChildren(); for (UIComponent child : children) { - try { - context.getAttributes().put(SKIP_ITERATION_HINT, true); - Set hints = EnumSet.of(VisitHint.SKIP_ITERATION); - - VisitContext visitContext = VisitContext.createVisitContext(context, null, hints); - child.visitTree(visitContext, (visitContext1, component) -> { - VisitResult result = VisitResult.ACCEPT; + VisitContext visitContext = VisitContext.createVisitContext(context, null, SKIP_ITERATION_HINT); + child.visitTree(visitContext, (visitContext1, component) -> { + VisitResult result = VisitResult.ACCEPT; - if (isForm(component)) { - result = VisitResult.REJECT; - } else if (isInNeedOfForm(component)) { - addFormOmittedMessage(finalContext, component); - } - return result; - }); - } finally { - context.getAttributes().remove(SKIP_ITERATION_HINT); - } + if (isForm(component)) { + result = VisitResult.REJECT; + } else if (isInNeedOfForm(component)) { + addFormOmittedMessage(finalContext, component); + } + return result; + }); } } 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..2e335c0190 100644 --- a/impl/src/main/java/com/sun/faces/lifecycle/RestoreViewPhase.java +++ b/impl/src/main/java/com/sun/faces/lifecycle/RestoreViewPhase.java @@ -25,6 +25,7 @@ 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; @@ -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. */ @@ -382,14 +383,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 +396,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); } } From d59442dbc4dc9e2523e41c2251622fee9747e32a Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Thu, 30 Nov 2023 07:36:22 -0400 Subject: [PATCH 35/44] Remove comment --- .../faces/application/resource/FaceletWebappResourceHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a79c490b26..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 @@ -120,7 +120,7 @@ public Stream getViewResources(FacesContext facesContext, String path, i new ResourcePathsIterator(path, maxDepth, configuredExtensions, getRestrictedDirectories(options), facesContext.getExternalContext()), DISTINCT), false); Stream programmaticViewResources = Util.getCdiBeanManager(facesContext) - .getBeans(Object.class, Any.Literal.INSTANCE).stream() // The value is not @Nonbinding. Only in 4.1 we can use View.Literal.INSTANCE as qualifier. + .getBeans(Object.class, Any.Literal.INSTANCE).stream() .map(bean -> bean.getBeanClass().getAnnotation(View.class)) .filter(Objects::nonNull) .map(View::value); From aedf85725413c0e58c893b3ae187092898f11910 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 20 Jan 2024 12:40:44 -0400 Subject: [PATCH 36/44] Remove hardcoded string constant representing "UTF-8" where possible --- .../main/java/com/sun/faces/RIConstants.java | 4 ++- .../ConverterPropertyEditorFactory.java | 14 ++------ .../faces/context/ExternalContextImpl.java | 32 +++---------------- .../faces/context/PartialViewContextImpl.java | 11 ++----- .../com/sun/faces/context/flash/ELFlash.java | 22 +++---------- .../sun/faces/facelets/util/Classpath.java | 13 +++----- .../sun/faces/lifecycle/RestoreViewPhase.java | 13 ++------ .../com/sun/faces/renderkit/StateHelper.java | 12 ++----- .../html_basic/OutputLinkRenderer.java | 9 +++--- .../faces/context/PartialResponseWriter.java | 5 ++- 10 files changed, 35 insertions(+), 100 deletions(-) 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/ConverterPropertyEditorFactory.java b/impl/src/main/java/com/sun/faces/application/ConverterPropertyEditorFactory.java index 660e566abb..9f9d08b41d 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,7 +24,6 @@ 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; @@ -80,7 +80,6 @@ private static class Utf8InfoRef { int length; public Utf8InfoRef(int index, int length) { - super(); this.index = index; this.length = length; } @@ -102,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; - 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); diff --git a/impl/src/main/java/com/sun/faces/context/ExternalContextImpl.java b/impl/src/main/java/com/sun/faces/context/ExternalContextImpl.java index 13e505874b..935754bf91 100644 --- a/impl/src/main/java/com/sun/faces/context/ExternalContextImpl.java +++ b/impl/src/main/java/com/sun/faces/context/ExternalContextImpl.java @@ -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("\n"); pwriter.startDocument(); pwriter.redirect(requestURI); pwriter.endDocument(); @@ -974,14 +973,7 @@ public boolean isSecure() { @Override public String encodeBookmarkableURL(String baseUrl, Map> 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(); @@ -990,14 +982,7 @@ public String encodeBookmarkableURL(String baseUrl, Map> pa @Override public String encodeRedirectURL(String baseUrl, Map> 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(); @@ -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()); } @@ -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(); diff --git a/impl/src/main/java/com/sun/faces/context/PartialViewContextImpl.java b/impl/src/main/java/com/sun/faces/context/PartialViewContextImpl.java index f9b379ff53..bbf59f37c8 100644 --- a/impl/src/main/java/com/sun/faces/context/PartialViewContextImpl.java +++ b/impl/src/main/java/com/sun/faces/context/PartialViewContextImpl.java @@ -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; @@ -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("\n"); writer.startDocument(); if (isResetValues()) { @@ -322,7 +317,7 @@ public void processPartial(PhaseId phaseId) { } } } - + private void doFlashPostPhaseActions(FacesContext ctx) { try { ctx.getExternalContext().getFlash().doPostPhaseActions(ctx); @@ -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"); } diff --git a/impl/src/main/java/com/sun/faces/context/flash/ELFlash.java b/impl/src/main/java/com/sun/faces/context/flash/ELFlash.java index 85a728229f..c653e4627d 100644 --- a/impl/src/main/java/com/sun/faces/context/flash/ELFlash.java +++ b/impl/src/main/java/com/sun/faces/context/flash/ELFlash.java @@ -18,8 +18,8 @@ import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableDistributable; import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.ForceAlwaysWriteFlashCookie; +import static java.nio.charset.StandardCharsets.UTF_8; -import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.InvalidKeyException; @@ -640,7 +640,7 @@ public void doPostPhaseActions(FacesContext context) { * necessary because the call to extContext.flushBuffer() is too late, the response has already been committed by that * point. outgoingResponseIsRedirect is false. *

- * + * * @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/util/Classpath.java b/impl/src/main/java/com/sun/faces/facelets/util/Classpath.java index ac2bbce3b3..829cf0c20f 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; @@ -45,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 } @@ -93,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(), "UTF-8")), suffix); + boolean searchDone = searchDir(all, new File(URLDecoder.decode(url.getFile(), UTF_8)), suffix); if (!searchDone) { searchFromURL(all, prefix, suffix, url); } @@ -244,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, "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/RestoreViewPhase.java b/impl/src/main/java/com/sun/faces/lifecycle/RestoreViewPhase.java index 2e335c0190..74af50b913 100644 --- a/impl/src/main/java/com/sun/faces/lifecycle/RestoreViewPhase.java +++ b/impl/src/main/java/com/sun/faces/lifecycle/RestoreViewPhase.java @@ -29,10 +29,10 @@ 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; @@ -249,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); 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 6756c4028c..43380f0db1 100644 --- a/impl/src/main/java/com/sun/faces/renderkit/StateHelper.java +++ b/impl/src/main/java/com/sun/faces/renderkit/StateHelper.java @@ -21,11 +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.io.UnsupportedEncodingException; import java.net.URLEncoder; -import java.util.logging.Level; import java.util.logging.Logger; import com.sun.faces.RIConstants; @@ -113,12 +112,7 @@ public static void createAndStoreCryptographicallyStrongTokenInSession(HttpSessi ByteArrayGuardAESCTR guard = new ByteArrayGuardAESCTR(); String clearText = "" + System.currentTimeMillis(); String result = guard.encrypt(clearText); - try { - result = URLEncoder.encode(result, "UTF-8"); - } catch (UnsupportedEncodingException e) { - LOGGER.log(Level.SEVERE, "Unable to URL encode cryptographically strong token, storing clear text in session instead.", e); - result = clearText; - } + result = URLEncoder.encode(result, UTF_8); session.setAttribute(TOKEN_NAME, result); } @@ -173,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/OutputLinkRenderer.java b/impl/src/main/java/com/sun/faces/renderkit/html_basic/OutputLinkRenderer.java index 2bb779516d..750b7668e4 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,6 +19,7 @@ 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; @@ -35,7 +36,7 @@ /** * OutputLinkRenderer is a class ... - * + * * Lifetime And Scope * */ @@ -133,7 +134,7 @@ protected Object getValue(UIComponent component) { if (componentIsDisabled(component)) { return null; } - + return ((UIOutput) component).getValue(); } @@ -176,10 +177,10 @@ protected void renderAsActive(FacesContext context, UIComponent component) throw if (pn != null && pn.length() != 0) { String pv = paramList[i].value; sb.append(paramWritten ? '&' : '?'); - sb.append(URLEncoder.encode(pn, "UTF-8")); + sb.append(URLEncoder.encode(pn, UTF_8)); sb.append('='); if (pv != null && pv.length() != 0) { - sb.append(URLEncoder.encode(pv, "UTF-8")); + sb.append(URLEncoder.encode(pv, UTF_8)); } paramWritten = true; } 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); From 84f890583e94b9b6780417313fb134b7ada13854 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 20 Jan 2024 15:40:45 -0400 Subject: [PATCH 37/44] Review/cleanup Facelets/Request/Response encoding implementation https://github.com/eclipse-ee4j/mojarra/issues/5383 --- .../view/FaceletViewHandlingStrategy.java | 71 +++-------------- .../application/view/MultiViewHandler.java | 22 +----- .../com/sun/faces/context/UrlBuilder.java | 5 +- .../main/java/com/sun/faces/util/Util.java | 77 ++++++++++++++++++- .../faces/application/ViewHandler.java | 13 +++- 5 files changed, 100 insertions(+), 88 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java b/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java index 4075ffb474..444082f30b 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; @@ -904,26 +904,29 @@ protected ResponseWriter createResponseWriter(FacesContext context) throws IOExc // get our content type String contentType = (String) context.getAttributes().get("facelets.ContentType"); - // get the encoding + // get the last calculated encoding 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 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 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 diff --git a/impl/src/main/java/com/sun/faces/application/view/MultiViewHandler.java b/impl/src/main/java/com/sun/faces/application/view/MultiViewHandler.java index bb131daa0f..89f34122ca 100644 --- a/impl/src/main/java/com/sun/faces/application/view/MultiViewHandler.java +++ b/impl/src/main/java/com/sun/faces/application/view/MultiViewHandler.java @@ -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; @@ -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; @@ -350,24 +347,7 @@ public boolean removeProtectedView(String urlPattern) { */ @Override public String getRedirectURL(FacesContext context, String viewId, Map> 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> decodedParameters = new HashMap<>(); diff --git a/impl/src/main/java/com/sun/faces/context/UrlBuilder.java b/impl/src/main/java/com/sun/faces/context/UrlBuilder.java index d51c3fb83d..e593f78d2e 100644 --- a/impl/src/main/java/com/sun/faces/context/UrlBuilder.java +++ b/impl/src/main/java/com/sun/faces/context/UrlBuilder.java @@ -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; @@ -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 NULL_LIST = Arrays.asList((String) null); @@ -73,7 +74,7 @@ public UrlBuilder(String url, String encoding) { } public UrlBuilder(String url) { - this(url, DEFAULT_ENCODING); + this(url, CHAR_ENCODING); } // ---------------------------------------------------------- Public Methods 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 57e2863a38..e5df94e0ec 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -19,6 +19,7 @@ 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; @@ -27,9 +28,11 @@ 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; @@ -110,7 +113,7 @@ /** * Util is a class ... - * + * * Lifetime And Scope * */ @@ -1618,4 +1621,76 @@ 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. + return encoding; + } + } + + // 2. If none found then get it from context (this is usually set during compile/buildtime based on request character encoding). + 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 (this is unexpected! could only happen with a broken Facelets Compiler or ViewHandler) then get it from request. + 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 (also unexpected! could only happen with a broken HttpServletRequestWrapper) then get previously known request encoding from session. + 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 c63e37e8a3..95c67f65d2 100644 --- a/impl/src/main/java/jakarta/faces/application/ViewHandler.java +++ b/impl/src/main/java/jakarta/faces/application/ViewHandler.java @@ -228,12 +228,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); @@ -491,7 +496,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. From e0758c01e08eb6eeea2ad3ec460e48f04b9a5120 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 21 Jan 2024 11:34:19 -0400 Subject: [PATCH 38/44] Further clarified comments after self-review. --- .../faces/application/view/FaceletViewHandlingStrategy.java | 2 +- impl/src/main/java/com/sun/faces/util/Util.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java b/impl/src/main/java/com/sun/faces/application/view/FaceletViewHandlingStrategy.java index 444082f30b..667e21b5e4 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 @@ -904,7 +904,7 @@ protected ResponseWriter createResponseWriter(FacesContext context) throws IOExc // get our content type String contentType = (String) context.getAttributes().get("facelets.ContentType"); - // get the last calculated encoding + // get the encoding String encoding = (String) context.getAttributes().get(FACELETS_ENCODING_KEY); // Create a dummy ResponseWriter with a bogus writer, 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 e5df94e0ec..ca35f1100c 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -1642,11 +1642,13 @@ public static String getResponseEncoding(FacesContext context, Optional 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)) { @@ -1655,6 +1657,7 @@ public static String getResponseEncoding(FacesContext context, Optional if (encoding == null) { // 3. If none found (this is unexpected! could only happen with a broken Facelets Compiler or ViewHandler) then get it from request. + // See also ViewHandler#initView() and ViewHandler#calculateCharacterEncoding(). encoding = context.getExternalContext().getRequestCharacterEncoding(); if (encoding != null && LOGGER.isLoggable(FINEST)) { @@ -1664,6 +1667,7 @@ public static String getResponseEncoding(FacesContext context, Optional if (encoding == null && context.getExternalContext().getSession(false) != null) { // 4. If still none found (also unexpected! could only happen with a broken HttpServletRequestWrapper) 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)) { From 081e6ea797eacb6793bfe4e813e7e7dbda96d565 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 21 Jan 2024 16:48:19 -0400 Subject: [PATCH 39/44] UIInput#getSubmittedValue() must also consider isLocalValueSet() when INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL=true https://github.com/eclipse-ee4j/mojarra/issues/5386 --- impl/src/main/java/jakarta/faces/component/UIInput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From 263095b2d23e39de4709772af441a1bfde35855d Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 28 Jan 2024 08:24:00 -0400 Subject: [PATCH 40/44] Further cleanup of comments --- impl/src/main/java/com/sun/faces/util/Util.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 ca35f1100c..5289d39f71 100644 --- a/impl/src/main/java/com/sun/faces/util/Util.java +++ b/impl/src/main/java/com/sun/faces/util/Util.java @@ -1656,7 +1656,7 @@ public static String getResponseEncoding(FacesContext context, Optional } if (encoding == null) { - // 3. If none found (this is unexpected! could only happen with a broken Facelets Compiler or ViewHandler) then get it from request. + // 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(); @@ -1666,7 +1666,7 @@ public static String getResponseEncoding(FacesContext context, Optional } if (encoding == null && context.getExternalContext().getSession(false) != null) { - // 4. If still none found (also unexpected! could only happen with a broken HttpServletRequestWrapper) then get previously known request encoding from session. + // 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); From 5a23e8a47de102ccd4a11a69ce0b46e1bd1a25f3 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 10 Feb 2024 11:57:50 -0400 Subject: [PATCH 41/44] Add session mutex https://github.com/eclipse-ee4j/mojarra/issues/5380 --- .../view/ViewScopeContextManager.java | 7 ++++--- .../ClientWindowScopeContextManager.java | 3 ++- .../sun/faces/config/ConfigureListener.java | 6 ++++++ .../com/sun/faces/context/SessionMap.java | 19 +++++++++++++++++++ .../renderkit/ServerSideStateHelper.java | 11 ++++++----- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/application/view/ViewScopeContextManager.java b/impl/src/main/java/com/sun/faces/application/view/ViewScopeContextManager.java index 1dee095be2..ccad44342d 100644 --- a/impl/src/main/java/com/sun/faces/application/view/ViewScopeContextManager.java +++ b/impl/src/main/java/com/sun/faces/application/view/ViewScopeContextManager.java @@ -20,6 +20,7 @@ import static com.sun.faces.application.view.ViewScopeManager.VIEW_MAP_ID; import static com.sun.faces.cdi.CdiUtils.getBeanReference; import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableDistributable; +import static com.sun.faces.context.SessionMap.getMutex; import static com.sun.faces.util.Util.getCdiBeanManager; import static java.util.logging.Level.FINEST; import static java.util.logging.Level.WARNING; @@ -95,7 +96,7 @@ public void clear(FacesContext facesContext, Map viewMap) { LOGGER.log(WARNING, "Cannot locate the view map to clear in the active maps: {0}", viewMap); } } - + /** * Clear the given view map. * @@ -247,7 +248,7 @@ private Map getContextMap(FacesContext facesCont String viewMapId = (String) facesContext.getViewRoot().getTransientStateHelper().getTransient(VIEW_MAP_ID); if (activeViewScopeContexts == null && create) { - synchronized (session) { + synchronized (getMutex(session)) { activeViewScopeContexts = new ConcurrentHashMap<>(); sessionMap.put(ACTIVE_VIEW_CONTEXTS, activeViewScopeContexts); } @@ -332,7 +333,7 @@ public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { HttpSession session = httpSessionEvent.getSession(); - Map> activeViewScopeContexts = (Map>) + Map> activeViewScopeContexts = (Map>) session.getAttribute(ACTIVE_VIEW_CONTEXTS); if (activeViewScopeContexts != null) { Map activeViewMaps = (Map) session.getAttribute(ViewScopeManager.ACTIVE_VIEW_MAPS); diff --git a/impl/src/main/java/com/sun/faces/cdi/clientwindow/ClientWindowScopeContextManager.java b/impl/src/main/java/com/sun/faces/cdi/clientwindow/ClientWindowScopeContextManager.java index 5a2a9d4c90..6f6d234681 100644 --- a/impl/src/main/java/com/sun/faces/cdi/clientwindow/ClientWindowScopeContextManager.java +++ b/impl/src/main/java/com/sun/faces/cdi/clientwindow/ClientWindowScopeContextManager.java @@ -19,6 +19,7 @@ import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableDistributable; import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.NumberOfClientWindows; +import static com.sun.faces.context.SessionMap.getMutex; import static java.util.logging.Level.FINEST; import java.util.Collections; @@ -157,7 +158,7 @@ private Map getContextMap(FacesContext f numberOfClientWindows = Integer.valueOf(NumberOfClientWindows.getDefaultValue()); } - synchronized (session) { + synchronized (getMutex(session)) { sessionMap.put(CLIENT_WINDOW_CONTEXTS, Collections.synchronizedMap(new LRUMap(numberOfClientWindows))); } } diff --git a/impl/src/main/java/com/sun/faces/config/ConfigureListener.java b/impl/src/main/java/com/sun/faces/config/ConfigureListener.java index 7775923b4c..12661b51f3 100644 --- a/impl/src/main/java/com/sun/faces/config/ConfigureListener.java +++ b/impl/src/main/java/com/sun/faces/config/ConfigureListener.java @@ -27,6 +27,8 @@ import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.ForceLoadFacesConfigFiles; import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.VerifyFacesConfigObjects; import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.JakartaFacesProjectStage; +import static com.sun.faces.context.SessionMap.createMutex; +import static com.sun.faces.context.SessionMap.removeMutex; import static com.sun.faces.push.WebsocketEndpoint.URI_TEMPLATE; import static java.lang.Boolean.TRUE; import static java.text.MessageFormat.format; @@ -350,6 +352,8 @@ public void requestInitialized(ServletRequestEvent event) { @Override public void sessionCreated(HttpSessionEvent event) { + createMutex(event.getSession()); + if (webAppListener != null) { webAppListener.sessionCreated(event); } @@ -357,6 +361,8 @@ public void sessionCreated(HttpSessionEvent event) { @Override public void sessionDestroyed(HttpSessionEvent event) { + removeMutex(event.getSession()); + if (webAppListener != null) { webAppListener.sessionDestroyed(event); } diff --git a/impl/src/main/java/com/sun/faces/context/SessionMap.java b/impl/src/main/java/com/sun/faces/context/SessionMap.java index 9ccf3b3c8b..f6487bc0e2 100644 --- a/impl/src/main/java/com/sun/faces/context/SessionMap.java +++ b/impl/src/main/java/com/sun/faces/context/SessionMap.java @@ -37,6 +37,7 @@ public class SessionMap extends BaseContextMap { private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger(); + private static final String MUTEX = Mutex.class.getName(); private final HttpServletRequest request; private final ProjectStage stage; @@ -189,4 +190,22 @@ protected HttpSession getSession(boolean createNew) { return request.getSession(createNew); } + // ----------------------------------------------------------- Session Mutex + + private static final class Mutex implements Serializable { + private static final long serialVersionUID = 1L; + } + + public static void createMutex(HttpSession session) { + session.setAttribute(MUTEX, new Mutex()); + } + + public static Object getMutex(Object session) { + return session instanceof HttpSession ? ((HttpSession) session).getAttribute(MUTEX) : session; + } + + public static void removeMutex(HttpSession session) { + session.removeAttribute(MUTEX); + } + } // END SessionMap 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..2dc7a67719 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)); @@ -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); } From c5747dd198ff80a8c17167b20657850530f7eb9f Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 10 Feb 2024 12:30:46 -0400 Subject: [PATCH 42/44] Fallback in case there's no mutex attribute for whatever reason --- impl/src/main/java/com/sun/faces/context/SessionMap.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/impl/src/main/java/com/sun/faces/context/SessionMap.java b/impl/src/main/java/com/sun/faces/context/SessionMap.java index f6487bc0e2..d5ea75a3d8 100644 --- a/impl/src/main/java/com/sun/faces/context/SessionMap.java +++ b/impl/src/main/java/com/sun/faces/context/SessionMap.java @@ -16,6 +16,8 @@ package com.sun.faces.context; +import static java.util.Optional.ofNullable; + import java.io.Serializable; import java.util.Collections; import java.util.Enumeration; @@ -201,7 +203,7 @@ public static void createMutex(HttpSession session) { } public static Object getMutex(Object session) { - return session instanceof HttpSession ? ((HttpSession) session).getAttribute(MUTEX) : session; + return session instanceof HttpSession httpSession ? ofNullable(httpSession.getAttribute(MUTEX)).orElse(session) : session; } public static void removeMutex(HttpSession session) { From 86d086483c152f804e2de762858fcc29d342ae40 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 10 Feb 2024 12:42:04 -0400 Subject: [PATCH 43/44] Mmkay instanceof var was only available since Java 14 --- impl/src/main/java/com/sun/faces/context/SessionMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impl/src/main/java/com/sun/faces/context/SessionMap.java b/impl/src/main/java/com/sun/faces/context/SessionMap.java index d5ea75a3d8..b7c1ab7b74 100644 --- a/impl/src/main/java/com/sun/faces/context/SessionMap.java +++ b/impl/src/main/java/com/sun/faces/context/SessionMap.java @@ -203,7 +203,7 @@ public static void createMutex(HttpSession session) { } public static Object getMutex(Object session) { - return session instanceof HttpSession httpSession ? ofNullable(httpSession.getAttribute(MUTEX)).orElse(session) : session; + return session instanceof HttpSession ? ofNullable(((HttpSession) session).getAttribute(MUTEX)).orElse(session) : session; } public static void removeMutex(HttpSession session) { From 039c661cd0b6dc250d881064eeb134bf516f9677 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sun, 18 Feb 2024 10:33:34 -0400 Subject: [PATCH 44/44] Found and fixed two more occurrences on synchronized(session) https://github.com/eclipse-ee4j/mojarra/issues/5380 --- .../main/java/com/sun/faces/lifecycle/ClientWindowImpl.java | 3 ++- .../java/com/sun/faces/renderkit/ServerSideStateHelper.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) 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/renderkit/ServerSideStateHelper.java b/impl/src/main/java/com/sun/faces/renderkit/ServerSideStateHelper.java index 2dc7a67719..5c96f298aa 100644 --- a/impl/src/main/java/com/sun/faces/renderkit/ServerSideStateHelper.java +++ b/impl/src/main/java/com/sun/faces/renderkit/ServerSideStateHelper.java @@ -263,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);