If you try to access this http://int01.openmrs.org:8080/openmrs/ you will be welcomed by an ugly error message and stack trace, which does not give you a clue as to what is happening:
java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
This error happens whenever there are liquibase changesets that need to be run on an existing database. It puzzled me to find out that all is well when I run locally from Eclipse, and all the liquibase changesets are run properly. I then tried running from OpenMRS Standalone and I was eventually relieved to reproduce the exact same problem.
Now the question was, why doesn’t the same problem happen when I run from Eclipse? After spending some time investigating by running OpenMRS Standalone in debug mode, I noticed that the order of calling Filter.init()
was differing between Tomcat, and Jetty, which I was using when running from Eclipse. And our startup code is dependent on InitializationFilter.init()
being called before UpdateFilter.init()
. I also noticed that the Filter.doFilter()
calling order was similar for both Tomcat and Jetty, as the servlet spec guarantees. So we somehow assumed that it was the same for Filter.init()
, and it turned out to be so, though not guaranteed by the spec, until when we upgraded to Java 8.
On looking at the Tomcat source code, I noticed that for calling Filter.init()
, they iterate over a HashMap which, according to the spec, does not guarantee maintaining the order of insertion. But before Java 8, the order was somehow maintained because HashMaps were implemented as linked lists. With Java 8, to improve their performance, they were changed to balanced trees, hence the effects. The spec not being affected by this change in internal implementation.
Jetty maintains the same order for Filter.init()
because it iterates over the same data structure (an array) as it uses for calling Filter.doFilter()
. Tomcat also iterates over array when calling Filter.doFilter()
, hence fulfilling the spec, but a HashMap when calling Filter.init()
, since the spec is silent about Filter.init()
call order.
I guess we all know the moral of the story!