in tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassLoaderContextManagerImpl.java [210:324]
private PageClassLoaderContext processUsingDependencies(
String className,
PageClassLoaderContext root,
Supplier<PageClassLoaderContext> unknownContextProvider,
Function<ClassLoader, PlasticProxyFactory> plasticProxyFactoryProvider,
Set<String> classesToInvalidate,
Set<String> alreadyProcessed,
boolean processCircularDependencies)
{
PageClassLoaderContext context = root.findByClassName(className);
if (context == null)
{
// Class isn't in a controlled package, so it doesn't get transformed
// and should go for the root context, which is never thrown out.
if (!root.getPlasticManager().shouldInterceptClassLoading(className))
{
context = root;
} else {
if (
!componentDependencyRegistry.contains(className) ||
!multipleClassLoaders
// TODO: review this
// && componentDependencyRegistry.getDependents(className).isEmpty()
)
{
context = unknownContextProvider.get();
}
else
{
alreadyProcessed.add(className);
// Sorting dependencies alphabetically so we have consistent results.
List<String> dependencies = new ArrayList<>(getDependenciesWithoutPages(className));
Collections.sort(dependencies);
// Process dependencies depth-first
for (String dependency : dependencies)
{
// Avoid infinite recursion loops
if (!alreadyProcessed.contains(dependency)/* &&
!circularDependencies.contains(dependency)*/)
{
processUsingDependencies(dependency, root, unknownContextProvider,
plasticProxyFactoryProvider, classesToInvalidate, alreadyProcessed, false);
}
}
// Collect context dependencies
Set<PageClassLoaderContext> contextDependencies = new HashSet<>();
for (String dependency : dependencies)
{
PageClassLoaderContext dependencyContext = root.findByClassName(dependency);
if (dependencyContext == null)
{
dependencyContext = processUsingDependencies(dependency, root, unknownContextProvider,
plasticProxyFactoryProvider, classesToInvalidate, alreadyProcessed);
}
if (!dependencyContext.isRoot())
{
contextDependencies.add(dependencyContext);
}
}
if (contextDependencies.size() == 0)
{
context = new PageClassLoaderContext(
getContextName(className),
root,
Collections.singleton(className),
plasticProxyFactoryProvider.apply(root.getClassLoader()),
this::get);
}
else
{
PageClassLoaderContext parentContext;
if (contextDependencies.size() == 1)
{
parentContext = contextDependencies.iterator().next();
}
else
{
parentContext = merge(contextDependencies, plasticProxyFactoryProvider, root, classesToInvalidate);
}
context = new PageClassLoaderContext(
getContextName(className),
parentContext,
Collections.singleton(className),
plasticProxyFactoryProvider.apply(parentContext.getClassLoader()),
this::get);
}
context.getParent().addChild(context);
// Ensure non-page class is initialized in the correct context and classloader.
// Pages get their own context and classloader, so this initialization
// is both non-needed and a cause for an NPE if it happens.
if (!componentClassResolver.isPage(className)
|| componentDependencyRegistry.getDependencies(className, DependencyType.USAGE).isEmpty())
{
loadClass(className, context);
}
LOGGER.debug("New context: {}", context);
}
}
}
context.addClass(className);
return context;
}