You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ta...@apache.org on 2026/03/26 13:38:13 UTC
(myfaces) branch main updated: MYFACES-4589 / MYFACES-4577 / MYFACES-4585: improve facelets compatibility
This is an automated email from the ASF dual-hosted git repository.
tandraschko pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces.git
The following commit(s) were added to refs/heads/main by this push:
new 2071acdd2 MYFACES-4589 / MYFACES-4577 / MYFACES-4585: improve facelets compatibility
2071acdd2 is described below
commit 2071acdd248eca82fd1f2a785b43cfb61764484f
Author: tandraschko <ta...@apache.org>
AuthorDate: Thu Mar 26 14:38:01 2026 +0100
MYFACES-4589 / MYFACES-4577 / MYFACES-4585: improve facelets compatibility
---
.../facelets/el/TaglibAttributeVariableMapper.java | 48 +++++++++++++
.../view/facelets/el/VariableMapperWrapper.java | 2 +-
.../view/facelets/impl/DefaultFaceletContext.java | 34 ++++++++-
.../view/facelets/impl/TemplateContextImpl.java | 44 ++++++++++++
.../view/facelets/tag/LegacyUserTagHandler.java | 5 +-
.../myfaces/view/facelets/tag/UserTagHandler.java | 29 +++++++-
.../CompositeComponentResourceTagHandler.java | 7 +-
.../view/facelets/tag/ui/CompositionHandler.java | 8 +++
.../view/facelets/tag/ui/IncludeHandler.java | 4 +-
...gHandlerCompositeBeanShadowing4589TestCase.java | 69 +++++++++++++++++++
.../tag/UserTagHandlerVariableMapperTestCase.java | 80 ++++++++++++++++++++++
.../tag/ui/IncludeHandler4577TestCase.java | 70 +++++++++++++++++++
.../facelets/tag/resources/components/cc4589.xhtml | 17 +++++
.../tag/ui/myfaces4577_header_fragment.xhtml | 27 ++++++++
.../facelets/tag/ui/myfaces4577_header_shell.xhtml | 28 ++++++++
.../view/facelets/tag/ui/myfaces4577_main.xhtml | 34 +++++++++
.../facelets/tag/ui/myfaces4577_template.xhtml | 30 ++++++++
.../myfaces/view/facelets/tag/user4585.taglib.xml | 31 +++++++++
.../myfaces/view/facelets/tag/user4585_main.xhtml | 31 +++++++++
.../myfaces/view/facelets/tag/user4585_panel.xhtml | 25 +++++++
.../myfaces/view/facelets/tag/user4589.taglib.xml | 31 +++++++++
.../myfaces/view/facelets/tag/user4589_main.xhtml | 20 ++++++
.../myfaces/view/facelets/tag/user4589_panel.xhtml | 23 +++++++
23 files changed, 688 insertions(+), 9 deletions(-)
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/el/TaglibAttributeVariableMapper.java b/impl/src/main/java/org/apache/myfaces/view/facelets/el/TaglibAttributeVariableMapper.java
new file mode 100644
index 000000000..b774ac6a6
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/el/TaglibAttributeVariableMapper.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.view.facelets.el;
+
+import jakarta.el.VariableMapper;
+
+/**
+ * Marks VariableMapper layers that bind Facelets taglib attribute short names
+ * ({@link org.apache.myfaces.view.facelets.tag.UserTagHandler}). Such bindings must not
+ * apply inside nested composite component implementations (MYFACES-4589), where identifiers
+ * like {@code #{color}} must resolve to managed beans (e.g. CDI) instead of shadowing
+ * ancestor taglib attributes named {@code color}.
+ */
+public final class TaglibAttributeVariableMapper extends VariableMapperWrapper
+{
+ public TaglibAttributeVariableMapper(VariableMapper orig)
+ {
+ super(orig);
+ }
+
+ /**
+ * Removes all taglib attribute mapper layers from the top of the chain.
+ */
+ public static VariableMapper unwrapTaglibAttributeScopes(VariableMapper mapper)
+ {
+ while (mapper instanceof TaglibAttributeVariableMapper)
+ {
+ mapper = ((TaglibAttributeVariableMapper) mapper).getWrapped();
+ }
+ return mapper;
+ }
+}
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/el/VariableMapperWrapper.java b/impl/src/main/java/org/apache/myfaces/view/facelets/el/VariableMapperWrapper.java
index da5d57aa6..1cf75581f 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/el/VariableMapperWrapper.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/el/VariableMapperWrapper.java
@@ -34,7 +34,7 @@
* @author Jacob Hookom
* @version $Id$
*/
-public final class VariableMapperWrapper extends VariableMapperBase implements FacesWrapper<VariableMapper>
+public class VariableMapperWrapper extends VariableMapperBase implements FacesWrapper<VariableMapper>
{
private final VariableMapper _target;
private final VariableMapperBase _targetBase;
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/impl/DefaultFaceletContext.java b/impl/src/main/java/org/apache/myfaces/view/facelets/impl/DefaultFaceletContext.java
index f25598775..78546e86a 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/impl/DefaultFaceletContext.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/impl/DefaultFaceletContext.java
@@ -420,8 +420,38 @@ public void extendClient(final TemplateClient client)
public boolean includeDefinition(UIComponent parent, String name)
throws IOException, FaceletException, FacesException, ELException
{
- return _isolatedTemplateContext.get(_currentTemplateContext).includeDefinition(
- this, this._facelet, parent, name);
+ // Walk template contexts from innermost outward so ui:insert inside ui:include can
+ // resolve ui:define from an enclosing composition (MYFACES-4577). Stop when a
+ // composite-component layer is reached: outer Facelets definitions must not apply
+ // inside composite implementation (TemplateContext javadoc).
+ int i = _currentTemplateContext;
+ while (i >= 0)
+ {
+ TemplateContext tc = _isolatedTemplateContext.get(i);
+ if (tc.includeDefinition(this, this._facelet, parent, name))
+ {
+ return true;
+ }
+ if (tc.getCompositeComponentClient() != null)
+ {
+ break;
+ }
+ // If this layer already has a templating ui:composition (template="..."), do not
+ // merge outer template clients: inner ui:insert must use default body or inner
+ // defines only (multilevel_template_* tests).
+ if (i > 0 && ((TemplateContextImpl) tc).hasTemplatingCompositionExtended())
+ {
+ break;
+ }
+ // Only cross this boundary for ui:include isolation layers (MYFACES-4577). Nested
+ // user-tag or other TemplateContext pushes must not inherit outer ui:define names.
+ if (i > 0 && !((TemplateContextImpl) tc).isIncludeIsolationLayer())
+ {
+ break;
+ }
+ i--;
+ }
+ return false;
}
@Override
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/impl/TemplateContextImpl.java b/impl/src/main/java/org/apache/myfaces/view/facelets/impl/TemplateContextImpl.java
index 4e346f6f3..c60ad68ea 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/impl/TemplateContextImpl.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/impl/TemplateContextImpl.java
@@ -44,6 +44,7 @@
import org.apache.myfaces.view.facelets.TemplateClient;
import org.apache.myfaces.view.facelets.TemplateContext;
import org.apache.myfaces.view.facelets.TemplateManager;
+import org.apache.myfaces.view.facelets.tag.ui.CompositionHandler;
/**
*
@@ -75,9 +76,47 @@ public class TemplateContextImpl extends TemplateContext
private final TemplateClientKnownParameterMap _templateClientKnownParameterMap;
+ /**
+ * True for layers pushed by {@code ui:include} to isolate params; used so
+ * {@link org.apache.myfaces.view.facelets.impl.DefaultFaceletContext#includeDefinition}
+ * only merges outer template clients across this boundary (MYFACES-4577), not across
+ * user-tag or other {@link org.apache.myfaces.view.facelets.AbstractFaceletContext#pushTemplateContext}
+ * uses (nested taglib tests).
+ */
+ private final boolean _includeIsolationLayer;
+
+ /**
+ * @return true if any client is a templating {@link CompositionHandler} (template attribute set)
+ */
+ boolean hasTemplatingCompositionExtended()
+ {
+ for (TemplateManagerImpl tm : _clients)
+ {
+ if (tm.isTemplatingComposition())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean isIncludeIsolationLayer()
+ {
+ return _includeIsolationLayer;
+ }
+
public TemplateContextImpl()
+ {
+ this(false);
+ }
+
+ /**
+ * @param includeIsolationLayer {@code true} when this context is pushed by {@code ui:include}
+ */
+ public TemplateContextImpl(boolean includeIsolationLayer)
{
super();
+ _includeIsolationLayer = includeIsolationLayer;
_clients = new ArrayList<>(4);
// Parameters registered using ui:param now are bound to template manager instances, because
// it should follow the same rules as template clients registered here. For example, to resolve
@@ -254,6 +293,11 @@ public boolean isRoot()
{
return this._root;
}
+
+ boolean isTemplatingComposition()
+ {
+ return _target instanceof CompositionHandler && ((CompositionHandler) _target).isTemplating();
+ }
}
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/LegacyUserTagHandler.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/LegacyUserTagHandler.java
index a350be213..d4791943b 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/LegacyUserTagHandler.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/LegacyUserTagHandler.java
@@ -38,7 +38,7 @@
import jakarta.faces.view.facelets.TagHandler;
import org.apache.myfaces.view.facelets.AbstractFaceletContext;
import org.apache.myfaces.view.facelets.TemplateClient;
-import org.apache.myfaces.view.facelets.el.VariableMapperWrapper;
+import org.apache.myfaces.view.facelets.el.TaglibAttributeVariableMapper;
import org.apache.myfaces.view.facelets.tag.ui.DefineHandler;
/**
@@ -111,7 +111,8 @@ public void apply(FaceletContext ctx, UIComponent parent) throws IOException, Fa
// setup a variable map
if (this._vars.length > 0)
{
- VariableMapper varMapper = new VariableMapperWrapper(orig);
+ VariableMapper varMapper = new TaglibAttributeVariableMapper(
+ TaglibAttributeVariableMapper.unwrapTaglibAttributeScopes(orig));
for (int i = 0; i < this._vars.length; i++)
{
varMapper.setVariable(names[i], values[i]);
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/UserTagHandler.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/UserTagHandler.java
index 56470762f..995965c7d 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/UserTagHandler.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/UserTagHandler.java
@@ -27,6 +27,7 @@
import jakarta.el.ELException;
import jakarta.el.ValueExpression;
+import jakarta.el.VariableMapper;
import jakarta.faces.FacesException;
import jakarta.faces.component.UIComponent;
import jakarta.faces.view.facelets.FaceletContext;
@@ -42,6 +43,7 @@
import org.apache.myfaces.view.facelets.TemplateClient;
import org.apache.myfaces.view.facelets.TemplateContext;
import org.apache.myfaces.view.facelets.el.FaceletStateValueExpression;
+import org.apache.myfaces.view.facelets.el.TaglibAttributeVariableMapper;
import org.apache.myfaces.view.facelets.impl.TemplateContextImpl;
import org.apache.myfaces.view.facelets.tag.faces.ComponentSupport;
import org.apache.myfaces.view.facelets.tag.faces.FaceletState;
@@ -96,6 +98,7 @@ public void apply(FaceletContext ctx, UIComponent parent) throws IOException, Fa
ELException
{
AbstractFaceletContext actx = (AbstractFaceletContext) ctx;
+ VariableMapper orig = ctx.getVariableMapper();
// eval include
try
{
@@ -120,7 +123,9 @@ public void apply(FaceletContext ctx, UIComponent parent) throws IOException, Fa
{
if (this._vars.length > 0)
{
- if (ELExpressionCacheMode.alwaysRecompile.equals(actx.getELExpressionCacheMode()))
+ boolean alwaysRecompile =
+ ELExpressionCacheMode.alwaysRecompile.equals(actx.getELExpressionCacheMode());
+ if (alwaysRecompile)
{
FaceletState faceletState = ComponentSupport.getFaceletState(ctx, parent, true);
for (int i = 0; i < this._vars.length; i++)
@@ -137,6 +142,24 @@ public void apply(FaceletContext ctx, UIComponent parent) throws IOException, Fa
((AbstractFaceletContext) ctx).getTemplateContext().setParameter(names[i], values[i]);
}
}
+
+ // MYFACES-4585: same taglib names must be on VariableMapper (as LegacyUserTagHandler does),
+ // so EL evaluated outside TemplateContext-only paths resolves correctly (e.g. nested components).
+ // MYFACES-4589 / nested taglib: do not delegate to ancestor user-tag attribute bindings.
+ VariableMapper varMapper = new TaglibAttributeVariableMapper(
+ TaglibAttributeVariableMapper.unwrapTaglibAttributeScopes(orig));
+ for (int i = 0; i < this._vars.length; i++)
+ {
+ if (alwaysRecompile)
+ {
+ varMapper.setVariable(names[i], new FaceletStateValueExpression(uniqueId, names[i]));
+ }
+ else
+ {
+ varMapper.setVariable(names[i], values[i]);
+ }
+ }
+ ctx.setVariableMapper(varMapper);
}
// Disable caching always, even in 'always' mode
@@ -150,6 +173,10 @@ public void apply(FaceletContext ctx, UIComponent parent) throws IOException, Fa
finally
{
fcc.endComponentUniqueIdSection();
+ if (this._vars.length > 0)
+ {
+ ctx.setVariableMapper(orig);
+ }
}
}
catch (FileNotFoundException e)
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentResourceTagHandler.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentResourceTagHandler.java
index 5fcb5abe7..ed914b7c8 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentResourceTagHandler.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentResourceTagHandler.java
@@ -59,6 +59,7 @@
import org.apache.myfaces.view.facelets.FaceletCompositionContext;
import org.apache.myfaces.view.facelets.TemplateClient;
import org.apache.myfaces.view.facelets.TemplateContext;
+import org.apache.myfaces.view.facelets.el.TaglibAttributeVariableMapper;
import org.apache.myfaces.view.facelets.el.VariableMapperWrapper;
import org.apache.myfaces.view.facelets.tag.ComponentContainerHandler;
import org.apache.myfaces.view.facelets.tag.TagHandlerUtils;
@@ -445,7 +446,11 @@ protected void applyCompositeComponentFacelet(FaceletContext faceletContext, UIC
VariableMapper orig = faceletContext.getVariableMapper();
try
{
- faceletContext.setVariableMapper(new VariableMapperWrapper(orig));
+ // MYFACES-4589: taglib UserTagHandler binds attribute short names on VariableMapper
+ // (MYFACES-4585). Those bindings must not shadow managed beans inside this composite
+ // implementation (e.g. #{color.blue} vs ancestor taglib attribute "color").
+ VariableMapper forComposite = TaglibAttributeVariableMapper.unwrapTaglibAttributeScopes(orig);
+ faceletContext.setVariableMapper(new VariableMapperWrapper(forComposite));
actx.pushCompositeComponentClient(this);
Resource resourceForCurrentView = faceletContext.getFacesContext().getApplication().
getResourceHandler().createResource(_resource.getResourceName(), _resource.getLibraryName());
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/CompositionHandler.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/CompositionHandler.java
index 43c971624..0060881c5 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/CompositionHandler.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/CompositionHandler.java
@@ -54,6 +54,14 @@ public final class CompositionHandler extends TagHandler implements TemplateClie
public final static String NAME = "composition";
+ /**
+ * @return true if this tag uses a template attribute (templating composition)
+ */
+ public boolean isTemplating()
+ {
+ return _template != null;
+ }
+
/**
* The resolvable URI of the template to use. The content within the composition tag will
* be used in populating the template specified.
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/IncludeHandler.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/IncludeHandler.java
index e2d26669a..8f988178c 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/IncludeHandler.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/IncludeHandler.java
@@ -199,7 +199,7 @@ else if (_params != null)
values[i] = _params[i].getValue(ctx);
}
- actx.pushTemplateContext(new TemplateContextImpl());
+ actx.pushTemplateContext(new TemplateContextImpl(true));
for (int i = 0; i < _params.length; i++)
{
@@ -208,7 +208,7 @@ else if (_params != null)
}
else
{
- actx.pushTemplateContext(new TemplateContextImpl());
+ actx.pushTemplateContext(new TemplateContextImpl(true));
}
if (url == null)
{
diff --git a/impl/src/test/java/org/apache/myfaces/view/facelets/tag/UserTagHandlerCompositeBeanShadowing4589TestCase.java b/impl/src/test/java/org/apache/myfaces/view/facelets/tag/UserTagHandlerCompositeBeanShadowing4589TestCase.java
new file mode 100644
index 000000000..37fbe1b60
--- /dev/null
+++ b/impl/src/test/java/org/apache/myfaces/view/facelets/tag/UserTagHandlerCompositeBeanShadowing4589TestCase.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.view.facelets.tag;
+
+import jakarta.el.ExpressionFactory;
+import jakarta.faces.application.ViewHandler;
+import jakarta.faces.component.UIViewRoot;
+
+import org.apache.myfaces.view.facelets.AbstractFaceletTestCase;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * MYFACES-4589: ancestor Facelets taglib attribute short names (e.g. {@code color}) must not
+ * shadow managed beans with the same identifier inside nested composite implementations.
+ */
+public class UserTagHandlerCompositeBeanShadowing4589TestCase extends AbstractFaceletTestCase
+{
+
+ @Override
+ protected void setUpServletObjects() throws Exception
+ {
+ super.setUpServletObjects();
+ servletContext.addInitParameter(ViewHandler.FACELETS_LIBRARIES_PARAM_NAME, "/user4589.taglib.xml");
+ }
+
+ @Override
+ protected ExpressionFactory createExpressionFactory()
+ {
+ return new org.apache.el.ExpressionFactoryImpl();
+ }
+
+ @Test
+ public void testBeanNotShadowedByTaglibAttributeInNestedComposite4589() throws Exception
+ {
+ facesContext.getExternalContext().getRequestMap().put("color", new ColorBean());
+
+ UIViewRoot root = facesContext.getViewRoot();
+ vdl.buildView(facesContext, root, "user4589_main.xhtml");
+
+ String out = render(root);
+ Assertions.assertTrue(out.contains("MYFACES4589_OK"),
+ "expected #{color.blue} to resolve bean, not taglib attribute: " + out);
+ }
+
+ public static class ColorBean
+ {
+ public String getBlue()
+ {
+ return "MYFACES4589_OK";
+ }
+ }
+}
diff --git a/impl/src/test/java/org/apache/myfaces/view/facelets/tag/UserTagHandlerVariableMapperTestCase.java b/impl/src/test/java/org/apache/myfaces/view/facelets/tag/UserTagHandlerVariableMapperTestCase.java
new file mode 100644
index 000000000..be316c9c0
--- /dev/null
+++ b/impl/src/test/java/org/apache/myfaces/view/facelets/tag/UserTagHandlerVariableMapperTestCase.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.view.facelets.tag;
+
+import jakarta.el.ExpressionFactory;
+import jakarta.faces.application.ViewHandler;
+import jakarta.faces.component.UIViewRoot;
+
+import org.apache.myfaces.view.facelets.AbstractFaceletTestCase;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * MYFACES-4585: Facelets taglib attributes must be exposed on {@link jakarta.el.VariableMapper}
+ * (as in {@code LegacyUserTagHandler}), not only on {@code TemplateContext}, so EL that is
+ * evaluated outside TemplateContext-only paths resolves correctly.
+ */
+public class UserTagHandlerVariableMapperTestCase extends AbstractFaceletTestCase
+{
+
+ @Override
+ protected void setUpServletObjects() throws Exception
+ {
+ super.setUpServletObjects();
+ servletContext.addInitParameter(ViewHandler.FACELETS_LIBRARIES_PARAM_NAME, "/user4585.taglib.xml");
+ }
+
+ @Override
+ protected ExpressionFactory createExpressionFactory()
+ {
+ return new org.apache.el.ExpressionFactoryImpl();
+ }
+
+ @Test
+ public void testTaglibAttributeAvailableViaVariableMapper4585() throws Exception
+ {
+ facesContext.getExternalContext().getRequestMap().put("user4585Bean", new User4585Bean());
+
+ UIViewRoot root = facesContext.getViewRoot();
+ vdl.buildView(facesContext, root, "user4585_main.xhtml");
+
+ String out = render(root);
+ Assertions.assertTrue(out.contains("OK:z"), "Expected taglib 'converter' to resolve for method call: " + out);
+ }
+
+ /**
+ * Mimics a JSF Converter (or delegate) passed into a taglib as in MYFACES-4585 / PrimeFaces AutoComplete.
+ */
+ public static class User4585Bean
+ {
+ public ConverterHelper getConverter()
+ {
+ return new ConverterHelper();
+ }
+ }
+
+ public static class ConverterHelper
+ {
+ public String getDisplayValue(String s)
+ {
+ return "OK:" + s;
+ }
+ }
+}
diff --git a/impl/src/test/java/org/apache/myfaces/view/facelets/tag/ui/IncludeHandler4577TestCase.java b/impl/src/test/java/org/apache/myfaces/view/facelets/tag/ui/IncludeHandler4577TestCase.java
new file mode 100644
index 000000000..1cb1583ad
--- /dev/null
+++ b/impl/src/test/java/org/apache/myfaces/view/facelets/tag/ui/IncludeHandler4577TestCase.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.view.facelets.tag.ui;
+
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.UIViewRoot;
+import jakarta.faces.component.html.HtmlForm;
+import jakarta.faces.component.html.HtmlOutputText;
+
+import org.apache.myfaces.renderkit.html.HtmlFormRenderer;
+import org.apache.myfaces.renderkit.html.HtmlTextRenderer;
+import org.apache.myfaces.view.facelets.AbstractFaceletTestCase;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * MYFACES-4577: ui:insert nested under ui:include must resolve the enclosing
+ * page's ui:define; components from the included fragment must be in the tree
+ * (e.g. for Ajax update targets).
+ */
+public class IncludeHandler4577TestCase extends AbstractFaceletTestCase
+{
+
+ @Override
+ protected void setupComponents() throws Exception
+ {
+ application.addComponent(UIViewRoot.COMPONENT_TYPE, UIViewRoot.class.getName());
+ application.addComponent(HtmlForm.COMPONENT_TYPE, HtmlForm.class.getName());
+ application.addComponent(HtmlOutputText.COMPONENT_TYPE, HtmlOutputText.class.getName());
+ }
+
+ @Override
+ protected void setupConvertersAndValidators() throws Exception
+ {
+ }
+
+ @Override
+ protected void setupRenderers() throws Exception
+ {
+ renderKit.addRenderer(HtmlForm.COMPONENT_FAMILY, "jakarta.faces.Form", new HtmlFormRenderer());
+ renderKit.addRenderer(HtmlOutputText.COMPONENT_FAMILY, "jakarta.faces.Text", new HtmlTextRenderer());
+ }
+
+ @Test
+ public void testInsertInsideIncludeSeesOuterDefine() throws Exception
+ {
+ UIViewRoot root = facesContext.getViewRoot();
+ vdl.buildView(facesContext, root, "myfaces4577_main.xhtml");
+
+ UIComponent toolbar = root.findComponent("form:toolbar");
+ Assertions.assertNotNull(toolbar, "toolbar from ui:include inside ui:define must be built");
+ Assertions.assertEquals("toolbar", ((HtmlOutputText) toolbar).getValue());
+ }
+}
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/resources/components/cc4589.xhtml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/resources/components/cc4589.xhtml
new file mode 100644
index 000000000..a8ad1f26c
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/resources/components/cc4589.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+-->
+<ui:component xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:cc="http://java.sun.com/jsf/composite"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:ui="http://java.sun.com/jsf/facelets">
+ <cc:interface/>
+ <cc:implementation>
+ <h:outputText id="out4589" value="#{color.blue}"/>
+ </cc:implementation>
+</ui:component>
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_header_fragment.xhtml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_header_fragment.xhtml
new file mode 100644
index 000000000..01a55b58a
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_header_fragment.xhtml
@@ -0,0 +1,27 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:ui="http://java.sun.com/jsf/facelets">
+
+ <h:outputText id="toolbar" value="toolbar"/>
+
+</ui:composition>
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_header_shell.xhtml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_header_shell.xhtml
new file mode 100644
index 000000000..54b0ca1d2
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_header_shell.xhtml
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets">
+
+ <div class="layout-header">
+ <ui:insert name="header"/>
+ </div>
+
+</ui:composition>
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_main.xhtml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_main.xhtml
new file mode 100644
index 000000000..bc30df8bf
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_main.xhtml
@@ -0,0 +1,34 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ MYFACES-4577: ui:insert inside ui:include must see the enclosing ui:composition's
+ ui:define (includeDefinition falls back to parent template contexts).
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ template="myfaces4577_template.xhtml">
+ <ui:define name="header">
+ <ui:include src="myfaces4577_header_fragment.xhtml"/>
+ </ui:define>
+ <ui:define name="content">
+ <span id="contentMarker">content</span>
+ </ui:define>
+</ui:composition>
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_template.xhtml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_template.xhtml
new file mode 100644
index 000000000..73aa8728a
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/ui/myfaces4577_template.xhtml
@@ -0,0 +1,30 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:ui="http://java.sun.com/jsf/facelets">
+
+ <h:form id="form">
+ <ui:include src="myfaces4577_header_shell.xhtml"/>
+ <ui:insert name="content"/>
+ </h:form>
+
+</ui:composition>
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4585.taglib.xml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4585.taglib.xml
new file mode 100644
index 000000000..83aa3b992
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4585.taglib.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<facelet-taglib version="2.0"
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd">
+
+ <namespace>http://myfaces.apache.org/test/user4585</namespace>
+
+ <tag>
+ <tag-name>panel</tag-name>
+ <source>user4585_panel.xhtml</source>
+ </tag>
+</facelet-taglib>
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4585_main.xhtml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4585_main.xhtml
new file mode 100644
index 000000000..b00495300
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4585_main.xhtml
@@ -0,0 +1,31 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!DOCTYPE html>
+<f:view xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:u="http://myfaces.apache.org/test/user4585">
+<html>
+<h:head/>
+<h:body>
+ <u:panel converter="#{user4585Bean.converter}"/>
+</h:body>
+</html>
+</f:view>
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4585_panel.xhtml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4585_panel.xhtml
new file mode 100644
index 000000000..50a9f3770
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4585_panel.xhtml
@@ -0,0 +1,25 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+ MYFACES-4585: taglib attribute must be visible on VariableMapper (not only TemplateContext).
+-->
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html">
+ <h:outputText id="out4585" value="#{converter.getDisplayValue('z')}"/>
+</ui:composition>
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4589.taglib.xml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4589.taglib.xml
new file mode 100644
index 000000000..8685257d5
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4589.taglib.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<facelet-taglib version="2.0"
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd">
+
+ <namespace>http://myfaces.apache.org/test/user4589</namespace>
+
+ <tag>
+ <tag-name>panel</tag-name>
+ <source>user4589_panel.xhtml</source>
+ </tag>
+</facelet-taglib>
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4589_main.xhtml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4589_main.xhtml
new file mode 100644
index 000000000..cd8df6176
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4589_main.xhtml
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!--
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+-->
+<f:view xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:u="http://myfaces.apache.org/test/user4589">
+<html>
+<h:head/>
+<h:body>
+ <u:panel color="red"/>
+</h:body>
+</html>
+</f:view>
diff --git a/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4589_panel.xhtml b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4589_panel.xhtml
new file mode 100644
index 000000000..46733c321
--- /dev/null
+++ b/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/user4589_panel.xhtml
@@ -0,0 +1,23 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:test="http://java.sun.com/jsf/composite/components">
+ <test:cc4589/>
+</ui:composition>