[jboss-jira] [JBoss JIRA] (WFLY-10539) ServletContainerInitializer instances aren't invoked in the specified order (WarMetaData order)

Paulo Silva (JIRA) issues at jboss.org
Fri Jun 8 07:44:00 EDT 2018


     [ https://issues.jboss.org/browse/WFLY-10539?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Paulo Silva updated WFLY-10539:
-------------------------------
    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 of the iteration order!

The set is being initialized here:

{code:java}
Set<ServletContainerInitializer> scis = scisMetaData.getScis();
Set<Class<? extends ServletContainerInitializer>> sciClasses = new HashSet<>();
if (scis == null) {
   scis = new HashSet<ServletContainerInitializer>();
   scisMetaData.setScis(scis);
}
{code}


And the ServletContainerInitializer implementations that are found in jars are being added here:

{code:java}
        // 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));
                }
            }
        }
{code}

and later iterated and added to the DeployementInfo here (they are added to a List!):

{code:java}
           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)));
                }
            }
{code}


+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 (List iteration) and called in DeployementManagerImpl:

{code:java}
//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();
    }
}
{code}

  was:
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.

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 of the iteration order!

The set is being initialized here:

{code:java}
Set<ServletContainerInitializer> scis = scisMetaData.getScis();
Set<Class<? extends ServletContainerInitializer>> sciClasses = new HashSet<>();
if (scis == null) {
   scis = new HashSet<ServletContainerInitializer>();
   scisMetaData.setScis(scis);
}
{code}


And the ServletContainerInitializer implementations that are found in jars are being added here:

{code:java}
        // 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));
                }
            }
        }
{code}

and later iterated and added to the DeployementInfo here (they are added to a List!):

{code:java}
           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)));
                }
            }
{code}


+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 (List iteration) and called in DeployementManagerImpl:

{code:java}
//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();
    }
}
{code}



> ServletContainerInitializer instances aren't invoked in the specified order (WarMetaData order)
> -----------------------------------------------------------------------------------------------
>
>                 Key: WFLY-10539
>                 URL: https://issues.jboss.org/browse/WFLY-10539
>             Project: WildFly
>          Issue Type: Bug
>          Components: Web (Undertow)
>    Affects Versions: 10.0.0.Final, 12.0.0.Final, 13.0.0.Final
>            Reporter: Paulo Silva
>            Assignee: Stuart Douglas
>
> 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 of the iteration order!
> The set is being initialized here:
> {code:java}
> Set<ServletContainerInitializer> scis = scisMetaData.getScis();
> Set<Class<? extends ServletContainerInitializer>> sciClasses = new HashSet<>();
> if (scis == null) {
>    scis = new HashSet<ServletContainerInitializer>();
>    scisMetaData.setScis(scis);
> }
> {code}
> And the ServletContainerInitializer implementations that are found in jars are being added here:
> {code:java}
>         // 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));
>                 }
>             }
>         }
> {code}
> and later iterated and added to the DeployementInfo here (they are added to a List!):
> {code:java}
>            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)));
>                 }
>             }
> {code}
> +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 (List iteration) and called in DeployementManagerImpl:
> {code:java}
> //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();
>     }
> }
> {code}



--
This message was sent by Atlassian JIRA
(v7.5.0#75005)


More information about the jboss-jira mailing list