Uploaded image for project: 'WildFly'
  1. WildFly
  2. WFLY-10539

ServletContainerInitializer instances aren't invoked in the specified order (WarMetaData order)

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Major Major
    • 14.0.0.Beta2
    • 10.0.0.Final, 12.0.0.Final, 13.0.0.Final
    • Web (Undertow)
    • None
    • Hide

      Define an absolute-ordering in web.xml and verify that ServletContainerInitializer
      implementations aren't being called in the expected order ( warMetaData.getOrder() )

      Show
      Define an absolute-ordering in web.xml and verify that ServletContainerInitializer implementations aren't being called in the expected order ( warMetaData.getOrder() )
    • Workaround Exists
    • Hide

      In ServletContainerInitializerDeploymentProcessor, update the following code to use a LinkedHashSet:

      if (scis == null) {
         scis = new LinkedHashSet<ServletContainerInitializer>();
         scisMetaData.setScis(scis);
      }
      
      Show
      In ServletContainerInitializerDeploymentProcessor, update the following code to use a LinkedHashSet: if (scis == null ) { scis = new LinkedHashSet<ServletContainerInitializer>(); scisMetaData.setScis(scis); }

      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();
          }
      }
      

              sdouglas1@redhat.com Stuart Douglas (Inactive)
              paulojmsilva Paulo Silva (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Created:
                Updated:
                Resolved: