in tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageSourceImpl.java [158:255]
public Page getPage(String canonicalPageName, boolean invalidateUnknownContext, Set<String> alreadyProcessed)
{
ComponentResourceSelector selector = selectorAnalyzer.buildSelectorForRequest();
CachedPageKey key = new CachedPageKey(canonicalPageName, selector);
// The while loop looks superfluous, but it helps to ensure that the Page instance,
// with all of its mutable construction-time state, is properly published to other
// threads (at least, as I understand Brian Goetz's explanation, it should be).
while (true)
{
Page page;
Object object = pageCache.get(key);
page = toPage(object);
if (page != null)
{
return page;
}
final String className = componentClassResolver.resolvePageNameToClassName(canonicalPageName);
if (multipleClassLoaders)
{
// Avoiding problems in PlasticClassPool.createTransformation()
// when the class being loaded has a page superclass
final List<String> pageDependencies = getPageDependencies(className);
CALL_STACK.get().add(className);
for (String dependencyClassName : pageDependencies)
{
// Avoiding infinite recursion caused by circular dependencies
if (!alreadyProcessed.contains(dependencyClassName) &&
!CALL_STACK.get().contains(className))
{
alreadyProcessed.add(dependencyClassName);
// Avoiding infinite recursion when, through component overriding,
// a dependency resolves to the same canonical page name as the
// one already requested in this call.
final String dependencyPageName = componentClassResolver.resolvePageClassNameToPageName(dependencyClassName);
final String resolvedDependencyPageClass = componentClassResolver.resolvePageNameToClassName(dependencyPageName);
if (!canonicalPageName.equals(dependencyPageName)
&& !className.equals(resolvedDependencyPageClass)
&& !isAbstract(dependencyClassName))
{
page = getPage(dependencyPageName,
invalidateUnknownContext, alreadyProcessed);
}
}
}
}
// In rare race conditions, we may see the same page loaded multiple times across
// different threads. The last built one will "evict" the others from the page cache,
// and the earlier ones will be GCed.
page = pageLoader.loadPage(canonicalPageName, selector);
final ReferenceType referenceType = pageCachingReferenceTypeService.get(canonicalPageName);
if (referenceType.equals(ReferenceType.SOFT))
{
pageCache.put(key, new SoftReference<Page>(page));
}
else
{
pageCache.put(key, page);
}
if (!productionMode)
{
final ComponentPageElement rootElement = page.getRootElement();
componentDependencyRegistry.clear(rootElement);
componentDependencyRegistry.register(rootElement.getComponent().getClass());
PageClassLoaderContext context = pageClassLoaderContextManager.get(className);
if (context.isUnknown() && multipleClassLoaders)
{
this.pageCache.remove(key);
if (invalidateUnknownContext)
{
pageClassLoaderContextManager.invalidateAndFireInvalidationEvents(context);
getPageDependencies(className);
}
context.getClassNames().clear();
// Avoiding bad invalidations
return getPage(canonicalPageName, false, alreadyProcessed);
}
}
}
}