Skip to content

Commit 1dd0a4a

Browse files
committed
[perf] some feedback from claude
1 parent d71def8 commit 1dd0a4a

21 files changed

Lines changed: 489 additions & 181 deletions

File tree

api/src/main/java/jakarta/faces/component/UIViewRoot.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import java.util.*;
2929
import java.util.logging.Level;
3030
import java.util.logging.Logger;
31-
import java.util.stream.Collectors;
3231

3332
import jakarta.el.MethodExpression;
3433
import jakarta.el.ValueExpression;
@@ -1814,14 +1813,16 @@ private void _processValidatorsDefault(FacesContext context)
18141813
*/
18151814
public Map<PhaseId, List<FacesEvent>> getQueuedEvents()
18161815
{
1817-
if (_events == null) {
1816+
if (_events == null || _events.isEmpty())
1817+
{
18181818
return Collections.emptyMap();
18191819
}
1820-
1821-
return _events.entrySet().stream().collect(
1822-
Collectors.toUnmodifiableMap(
1823-
Map.Entry::getKey,
1824-
entry -> Collections.unmodifiableList(entry.getValue())));
1820+
EnumMap<PhaseId, List<FacesEvent>> view = new EnumMap<>(PhaseId.class);
1821+
for (Map.Entry<PhaseId, List<FacesEvent>> e : _events.entrySet())
1822+
{
1823+
view.put(e.getKey(), Collections.unmodifiableList(e.getValue()));
1824+
}
1825+
return Collections.unmodifiableMap(view);
18251826
}
18261827

18271828
/**

impl/src/main/java/org/apache/myfaces/application/ResourceHandlerImpl.java

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ public class ResourceHandlerImpl extends ResourceHandler
9797
public final static String RENDERED_RESOURCES_SET = "org.apache.myfaces.RENDERED_RESOURCES_SET";
9898

9999
private static final String SHARED_STRING_BUILDER = ResourceHandlerImpl.class.getName() + ".SHARED_STRING_BUILDER";
100-
100+
101+
private static final String PROGRAMMATIC_VIEW_IDS_CACHE_KEY =
102+
ResourceHandlerImpl.class.getName() + ".PROGRAMMATIC_VIEW_IDS";
103+
101104
private static final String[] FACELETS_VIEW_MAPPINGS_PARAM = {ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME,
102105
"facelets.VIEW_MAPPINGS"};
103106

@@ -1727,32 +1730,58 @@ public Stream<String> getViewResources(FacesContext facesContext,
17271730
this._viewSuffixes = loadSuffixes(facesContext.getExternalContext());
17281731
}
17291732

1730-
Iterator it = new FilterInvalidSuffixViewResourceIterator(new ViewResourceIterator(facesContext,
1733+
Iterator<String> resourceIt = new FilterInvalidSuffixViewResourceIterator(
1734+
new ViewResourceIterator(facesContext,
17311735
getResourceHandlerSupport(), localePrefix, contracts,
17321736
contractPreferred, path, maxDepth, options), facesContext, _viewSuffixes);
17331737

1734-
return Stream.concat(StreamSupport.stream(Spliterators.spliteratorUnknownSize(it,Spliterator.DISTINCT),
1735-
false), getProgrammaticViewIds(facesContext));
1738+
Stream<String> resourceStream = StreamSupport.stream(
1739+
Spliterators.spliteratorUnknownSize(resourceIt, Spliterator.DISTINCT), false);
1740+
return Stream.concat(resourceStream, resolveProgrammaticViewIds(facesContext).stream());
17361741
}
17371742

1738-
private Stream<String> getProgrammaticViewIds(FacesContext facesContext)
1743+
/**
1744+
* CDI {@code @View} IDs; cached in the application map for the application lifetime.
1745+
*/
1746+
@SuppressWarnings("unchecked")
1747+
private List<String> resolveProgrammaticViewIds(FacesContext facesContext)
17391748
{
1740-
ArrayList<String> views = new ArrayList<String>();
1741-
BeanManager beanManager = CDIUtils.getBeanManager(facesContext);
1742-
if (beanManager != null)
1749+
Map<String, Object> appMap = facesContext.getExternalContext().getApplicationMap();
1750+
List<String> cached = (List<String>) appMap.get(PROGRAMMATIC_VIEW_IDS_CACHE_KEY);
1751+
if (cached != null)
1752+
{
1753+
return cached;
1754+
}
1755+
synchronized (appMap)
17431756
{
1744-
for (Bean<?> bean : beanManager.getBeans(Object.class, Any.Literal.INSTANCE))
1757+
cached = (List<String>) appMap.get(PROGRAMMATIC_VIEW_IDS_CACHE_KEY);
1758+
if (cached != null)
17451759
{
1746-
for (Annotation qualifier : bean.getQualifiers())
1760+
return cached;
1761+
}
1762+
BeanManager beanManager = CDIUtils.getBeanManager(facesContext);
1763+
if (beanManager == null)
1764+
{
1765+
cached = List.of();
1766+
}
1767+
else
1768+
{
1769+
ArrayList<String> views = new ArrayList<>();
1770+
for (Bean<?> bean : beanManager.getBeans(Object.class, Any.Literal.INSTANCE))
17471771
{
1748-
if (qualifier instanceof View view)
1772+
for (Annotation qualifier : bean.getQualifiers())
17491773
{
1750-
views.add(view.value());
1774+
if (qualifier instanceof View view)
1775+
{
1776+
views.add(view.value());
1777+
}
17511778
}
17521779
}
1780+
cached = List.copyOf(views);
17531781
}
1782+
appMap.put(PROGRAMMATIC_VIEW_IDS_CACHE_KEY, cached);
1783+
return cached;
17541784
}
1755-
return views.stream();
17561785
}
17571786

17581787
private Set<String> loadSuffixes(ExternalContext context)

impl/src/main/java/org/apache/myfaces/cdi/util/CDIUtils.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232
import java.lang.annotation.Annotation;
3333
import java.lang.reflect.Type;
3434
import java.util.Collections;
35+
import java.util.HashSet;
3536
import java.util.Objects;
3637
import java.util.Set;
37-
import java.util.stream.Collectors;
3838

3939
import org.apache.myfaces.webapp.FacesInitializerImpl;
4040

@@ -121,9 +121,15 @@ public static <T> T get(BeanManager beanManager, Type type, boolean create,
121121
Set<Bean<?>> beans = beanManager.getBeans(type, qualifiers);
122122
if (beanName != null)
123123
{
124-
beans = beans.stream()
125-
.filter(bean -> Objects.equals(beanName, getBeanName(bean)))
126-
.collect(Collectors.toSet());
124+
Set<Bean<?>> filtered = new HashSet<>();
125+
for (Bean<?> bean : beans)
126+
{
127+
if (Objects.equals(beanName, getBeanName(bean)))
128+
{
129+
filtered.add(bean);
130+
}
131+
}
132+
beans = filtered;
127133
}
128134
Bean<T> bean = (Bean<T>) beanManager.resolve(beans);
129135

impl/src/main/java/org/apache/myfaces/context/servlet/PartialViewContextImpl.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import jakarta.faces.context.PartialViewContext;
4747
import jakarta.faces.context.ResponseWriter;
4848
import jakarta.faces.event.BehaviorEvent;
49+
import jakarta.faces.event.FacesEvent;
4950
import jakarta.faces.event.PhaseId;
5051
import jakarta.faces.lifecycle.ClientWindow;
5152
import jakarta.faces.render.RenderKit;
@@ -93,6 +94,7 @@ public class PartialViewContextImpl extends PartialViewContext
9394
private Boolean _resetValues = null;
9495
private List<String> _evalScripts = new ArrayList<>();
9596
private List<AjaxBehavior> queuedAjaxBehaviors;
97+
private boolean queuedAjaxClearModel;
9698

9799
public PartialViewContextImpl(FacesContext context)
98100
{
@@ -392,13 +394,32 @@ public void processPartial(PhaseId phaseId)
392394
{
393395
if (phaseId == PhaseId.APPLY_REQUEST_VALUES)
394396
{
395-
queuedAjaxBehaviors = viewRoot.getQueuedEvents().values().stream().flatMap(List::stream)
396-
.filter(BehaviorEvent.class::isInstance)
397-
.map(BehaviorEvent.class::cast)
398-
.map(BehaviorEvent::getBehavior)
399-
.filter(AjaxBehavior.class::isInstance)
400-
.map(AjaxBehavior.class::cast)
401-
.toList();
397+
ArrayList<AjaxBehavior> ajaxList = new ArrayList<>();
398+
boolean clearModel = false;
399+
for (List<FacesEvent> events : viewRoot.getQueuedEvents().values())
400+
{
401+
if (events == null)
402+
{
403+
continue;
404+
}
405+
for (FacesEvent fe : events)
406+
{
407+
if (fe instanceof BehaviorEvent be)
408+
{
409+
var behavior = be.getBehavior();
410+
if (behavior instanceof AjaxBehavior ab)
411+
{
412+
ajaxList.add(ab);
413+
if (!clearModel && ab.isClearModel())
414+
{
415+
clearModel = true;
416+
}
417+
}
418+
}
419+
}
420+
}
421+
queuedAjaxBehaviors = ajaxList;
422+
queuedAjaxClearModel = clearModel;
402423
}
403424

404425
processPartialExecute(viewRoot, phaseId);
@@ -455,8 +476,7 @@ private void processPartialRendering(UIViewRoot viewRoot, PhaseId phaseId)
455476

456477
if (isResetValues())
457478
{
458-
boolean clearModel = queuedAjaxBehaviors != null
459-
&& queuedAjaxBehaviors.stream().anyMatch(AjaxBehavior::isClearModel);
479+
boolean clearModel = queuedAjaxBehaviors != null && queuedAjaxClearModel;
460480
viewRoot.resetValues(context, getRenderIds(), clearModel, RESET_VALUES_HINTS);
461481
}
462482

@@ -702,6 +722,7 @@ public void release()
702722
_renderAll = null;
703723
context = null;
704724
queuedAjaxBehaviors = null;
725+
queuedAjaxClearModel = false;
705726
_released = true;
706727
}
707728

impl/src/main/java/org/apache/myfaces/el/resolver/CompositeComponentELResolver.java

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,10 @@
3131

3232
import java.beans.BeanInfo;
3333
import java.beans.PropertyDescriptor;
34-
import java.lang.ref.WeakReference;
3534
import java.util.Collection;
35+
import java.util.IdentityHashMap;
3636
import java.util.Map;
3737
import java.util.Set;
38-
import java.util.WeakHashMap;
3938

4039
/**
4140
* Composite component attribute EL resolver. See Faces spec, section 5.6.2.2.
@@ -197,39 +196,25 @@ private Map<String, Object> _getCompositeComponentAttributesMapWrapper(
197196
{
198197
Map<Object, Object> contextMap = facesContext(elContext).getAttributes();
199198

200-
// We use a WeakHashMap<UIComponent, WeakReference<Map<String, Object>>> to
201-
// hold attribute map wrappers by two reasons:
202-
//
203-
// 1. The wrapper is used multiple times for a very short amount of time (in fact on current request).
204-
// 2. The original attribute map has an inner reference to UIComponent, so we need to wrap it
205-
// with WeakReference.
206-
//
207-
Map<UIComponent, WeakReference<Map<String, Object>>> compositeComponentAttributesMaps =
208-
(Map<UIComponent, WeakReference<Map<String, Object>>>) contextMap.get(COMPOSITE_COMPONENT_ATTRIBUTES_MAPS);
199+
// IdentityHashMap: FacesContext is not thread-safe; keys are UIComponent instances by identity.
200+
// The map lives only for the request (FacesContext attributes), avoiding WeakHashMap sync cost.
201+
IdentityHashMap<UIComponent, Map<String, Object>> compositeComponentAttributesMaps =
202+
(IdentityHashMap<UIComponent, Map<String, Object>>) contextMap.get(COMPOSITE_COMPONENT_ATTRIBUTES_MAPS);
209203

210204
Map<String, Object> attributesMap = null;
211-
WeakReference<Map<String, Object>> weakReference;
212205
if (compositeComponentAttributesMaps != null)
213206
{
214-
weakReference = compositeComponentAttributesMaps.get(baseComponent);
215-
if (weakReference != null)
216-
{
217-
attributesMap = weakReference.get();
218-
}
219-
if (attributesMap == null)
220-
{
221-
//create a wrapper map
222-
attributesMap = new CompositeComponentAttributesMapWrapper(baseComponent);
223-
compositeComponentAttributesMaps.put(baseComponent, new WeakReference<>(attributesMap));
224-
}
207+
attributesMap = compositeComponentAttributesMaps.get(baseComponent);
225208
}
226-
else
209+
if (attributesMap == null)
227210
{
228-
//Create both required maps
229211
attributesMap = new CompositeComponentAttributesMapWrapper(baseComponent);
230-
compositeComponentAttributesMaps = new WeakHashMap<>();
231-
compositeComponentAttributesMaps.put(baseComponent, new WeakReference<>(attributesMap));
232-
contextMap.put(COMPOSITE_COMPONENT_ATTRIBUTES_MAPS, compositeComponentAttributesMaps);
212+
if (compositeComponentAttributesMaps == null)
213+
{
214+
compositeComponentAttributesMaps = new IdentityHashMap<>(8);
215+
contextMap.put(COMPOSITE_COMPONENT_ATTRIBUTES_MAPS, compositeComponentAttributesMaps);
216+
}
217+
compositeComponentAttributesMaps.put(baseComponent, attributesMap);
233218
}
234219
return attributesMap;
235220
}

impl/src/main/java/org/apache/myfaces/lifecycle/LifecycleImpl.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public class LifecycleImpl extends Lifecycle
7474
* note in this case the semantic of the variable must be preserved.
7575
*/
7676
private volatile boolean _firstRequestProcessed = false;
77-
77+
7878
public LifecycleImpl()
7979
{
8080
// hide from public access
@@ -131,7 +131,7 @@ public void execute(FacesContext facesContext) throws FacesException
131131
new FacesConfigurator(facesContext.getExternalContext()).update();
132132
}
133133

134-
PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, getPhaseListeners());
134+
PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, phaseListeners);
135135
for (PhaseExecutor executor : lifecycleExecutors)
136136
{
137137
if (executePhase(facesContext, executor, phaseListenerMgr))
@@ -148,7 +148,7 @@ public PhaseExecutor getPhaseExecutor(PhaseId phaseId)
148148

149149
public boolean executePhase(FacesContext facesContext, PhaseId phaseId)
150150
{
151-
PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, getPhaseListeners());
151+
PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, phaseListeners);
152152
return executePhase(facesContext, getPhaseExecutor(phaseId), phaseListenerMgr);
153153
}
154154

@@ -242,7 +242,7 @@ public void render(FacesContext facesContext) throws FacesException
242242
log.finest("Entering " + renderExecutor.getPhase() + " in " + LifecycleImpl.class.getName());
243243
}
244244

245-
PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, getPhaseListeners());
245+
PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, phaseListeners);
246246
Flash flash = facesContext.getExternalContext().getFlash();
247247

248248
try

0 commit comments

Comments
 (0)