Details
-
Bug
-
Resolution: Done
-
Major
-
10.0.0.Final, 12.0.0.Final, 13.0.0.Final
-
None
-
-
Workaround Exists
-
Description
After several tests I've found that ServletContainerInitializer instances aren't being called in a deterministic order, even when an absolute-ordering is configured in web.xml. I've tested this in wildfly 10, 12 and 13.
Stepping through the code, on wildfly startup, I've found that ServletContainerInitializer instances are being collected to ScisMetaData.The problem is that they are being stored in a HashSet, and that makes no guarantees as to the iteration order!
The set is being initialized here:
Set<ServletContainerInitializer> scis = scisMetaData.getScis(); Set<Class<? extends ServletContainerInitializer>> sciClasses = new HashSet<>(); if (scis == null) { scis = new HashSet<ServletContainerInitializer>(); scisMetaData.setScis(scis); }
And the ServletContainerInitializer implementations that are found in jars are being added here:
// Find local ServletContainerInitializer services List<String> order = warMetaData.getOrder(); Map<String, VirtualFile> localScis = warMetaData.getScis(); if (order != null && localScis != null) { for (String jar : order) { VirtualFile sci = localScis.get(jar); if (sci != null) { scis.addAll(loadSci(classLoader, sci, jar, true, sciClasses)); } } }
and later iterated and added to the DeployementInfo here (they are added to a List!):
if (scisMetaData != null && scisMetaData.getHandlesTypes() != null) { for (final ServletContainerInitializer sci : scisMetaData.getScis()) { final ImmediateInstanceFactory<ServletContainerInitializer> instanceFactory = new ImmediateInstanceFactory<>(sci); d.addServletContainerInitalizer(new ServletContainerInitializerInfo(sci.getClass(), instanceFactory, scisMetaData.getHandlesTypes().get(sci))); } }
But, since scisMetaData.getScis() returns a HashSet, there is absolutely no guarantee that the iteration order is the same as the insertion order (the order that is imposed by warMetaData.getOrder()).
So a simple fix is to use a LinkedHashSet instead of a HashSet:
The ServletContainerInitializer instances are later iterated and called by insertion order (List iteration) in DeployementManagerImpl:
//then run the SCI's for (final ServletContainerInitializerInfo sci : deploymentInfo.getServletContainerInitializers()) { final InstanceHandle<? extends ServletContainerInitializer> instance = sci.getInstanceFactory().createInstance(); try { instance.getInstance().onStartup(sci.getHandlesTypes(), servletContext); } finally { instance.release(); } }