2

I need to migrate a web service application from Tomcat 6 (using JDK 1.5) to Tomcat 7 (using JDK 1.6) (actually testing on 7.0.27). The JAX-WS framework is Metro 2.1.

I'm using two ServletContextListeners:

  1. "WebAppListener" : to initialize the application (configuration, setup of database connections...).

  2. "com.sun.xml.ws.transport.http.servlet.WSServletContextListener": the Metro listener that creates the web services.

The order is important, because I need my web app fully initialized before the @PostConstruct method is called on my web service.

In Tomcat 6, this works marvelously:

INFO: Starting Servlet Engine: Apache Tomcat/6.0.36
mars 16, 2013 5:13:13 PM org.apache.catalina.startup.HostConfig deployWAR
INFO: Deploying web application archive WSWebApp.war
************************ IN WebAppListener.contextInitialized ***********************
************************ IN WSMyWebService.postConstruct ****************************

Exactly the same war however in Tomcat 7:

mars 16, 2013 6:45:24 PM org.apache.catalina.startup.HostConfig deployWAR
INFO: Deploying web application archive C:\home\tomcat\webapps\WSWebApp.war
************************ IN WSMyWebService.postConstruct ****************************
************************ IN WebAppListener.contextInitialized ***********************

I thought that the order in which the listener's are called is the order in which they appear in the web.xml?

Why this order has been changed in Tomcat 7? But more importantly: What do I need to do to get the "Tomcat6" order back?

Thanks.

estiedi
  • 384
  • 2
  • 12
  • This related question http://stackoverflow.com/questions/178562/how-to-determine-the-order-of-listeners-in-web-xml seems to confirm that the order in which the listeners are called is defined by the web.xml – estiedi Mar 17 '13 at 07:27
  • Servlet 3.0 spec says: 11.3.2 Deployment Declarations : Listener classes are declared in the Web application deployment descriptor using the listener element. **They are listed by class name in the order in which they are to be invoked** – estiedi Mar 17 '13 at 07:39
  • Created a simple web app with two listeners. Tomcat 7 behaves correctly : listeners are called in order of web.xml and when inverting the order, the calling order is inverted too. – estiedi Mar 17 '13 at 08:03
  • I narrowed the problem down to the spec version of the web.xml. Version 2.5 works as expected, version 3.0 does not. – estiedi Mar 17 '13 at 12:59

2 Answers2

3

The answer lies in a difference between Servlet spec 2.5 and Servlet spec 3.0.

In Servlet spec 2.5, the order in which listeners are called is defined by the order of their declaration in the deployment descriptor (web.xml):

SRV.10.3.3 Listener Registration.
The Web container creates an instance of each listener class and registers it for event notifications prior to the processing of the first request by the application. The Web container registers the listener instances according to the interfaces they implement and the order in which they appear in the deployment descriptor. During Web application execution, listeners are invoked in the order of their registration.

In Servlet spec 3.0, the order in which listeners are called is defined by the element <absolute-ordering> in the deployment descriptor (web.xml):

8.2.3 Assembling the descriptor from web.xml, webfragment.xml and annotations.
...
c. Prior to this release of the specification, context listeners were invoked in random order.

This is wrong IMHO, as stated above in the version 2.5 spec. :-)

As of Servlet 3.0, the listeners are invoked in the order in which they are declared in the web.xml as specified below:
i. Implementations of javax.servlet.ServletContextListener are invoked at their contextInitialized method in the order in which they have been declared, and at their contextDestroyed method in reverse order.

This doesn't work at all like you'd expect and is the source of my trouble, confusion and a lost weekend. ;-) It's NOT the same ordering as before (2.5 spec)!

But there is more... (the AHA moment is near!)

8.2.2 Ordering of web.xml and web-fragment.xml Since the specification allows the application configuration resources to be composed of multiple configuration files (web.xml and web-fragment.xml), discovered and loaded from several different places in the application, the question of ordering must be addressed.
...
Two cases must be considered to allow application configuration resources to express their ordering preferences.
1. Absolute ordering: an <absolute-ordering> element in the web.xml. a. In this case, ordering preferences that would have been handled by case 2 below must be ignored.
2. Relative ordering: an <ordering> element within the web-fragment.xml. a. A web-fragment.xml may have an <ordering> element. If so, this element must contain zero or one <before> element and zero or one <after> element. The meaning of these elements is explained below.

So, in my case, I had two options:

  1. Stick with version 2.5 : This works well on Tomcat 6 and 7, but is only moving problems to the future.
  2. Migrate to version 3.0 of the spec and adjust my deployment descriptor accordingly:

    • Update the version and schema in the web-app element, of course:

    <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    • Adding a <absolute-ordering> element that defines the order in which the listeners should be processed:

    <absolute-ordering> <name>bootstrap</name><name>ws</name><others/></absolute-ordering>
    <listener id="bootstrap">
       <listener-class>lu.estiedi.ws.WebAppBootstrap</listener-class>
    </listener>
       <listener id="ws">
    <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
    </listener>

Note the <others/> element : if it's not present, then the listeners that are not named will not be processed.

EDIT:

This doesn't actually solve the problem. It went away yesterday at home, but this morning at work the solution didn't work. :-(

It looks like Metro is using a service provider ServletContainerInitializer, which are called before the Listeners.

If this is the way it's supposed to work, then the PosConstruct method is always called before the contextInitialized on Listeners. Where do we initialise our application then, before the web services are created?

estiedi
  • 384
  • 2
  • 12
0

It is not about order of Listeners, it is about a new feature of servlets spec 3.0 and the fact that you are probably using a JAX-WS implementatios like METRO, I suppose. In servlets spec 3.0 (chapter 8.2.4) there is a way to add 3rd party plugins or APIs to containers (Tomcat in this case), for example JAX-WS, JAX-RS, JSF, etc. implementations. It is also known ad SCI (javax.servlet.ServletContainerInitializer). In short, METRO JAX-WS jars use this way so it is not mandatory to add listener (com.sun.xml.ws.transport.http.servlet.WSServletContextListener) and servlet configuration in your web.xml descriptor file. It uses sun-jaxws.xml file to list ws endpoints and instantiate ws classes before deployment of web application and execution, for example, of methods contextInitialized of listeners.

To avoid this, just follow instructions for avoiding SCI mechanism in Tomcat: How do I make Tomcat startup faster?

There exists an attribute on Context element, containerSciFilter. It can be used to disable container-provided features that are plugged into Tomcat via SCI API: WebSocket support (in Tomcat 7 and later), JSP support (in Tomcat 8 and later).

The class names to filter can be detected by looking into META-INF/services/javax.servlet.ServletContainerInitializer files in Tomcat JARs. For WebSocket support the name is org.apache.tomcat.websocket.server.WsSci, for JSP support the name is org.apache.jasper.servlet.JasperInitializer.

It worked for me with JAX-WS and it just uses web.xml config to deploy web services.