Supported versions of Tomcat?

At PIH, we’ve been running OpenMRS with Tomcat 7 for years now (with knowledge that we should really be upgrading to Tomcat 8 or Tomcat 9).

With the upgrade to OpenMRS 2.6.1, we’ve run into a issue when trying to hit the DELETE endpoint of REST Web Services module, and looking at the underlying error, it appears to be due to the fact that with in OpenMRS 2.6.1 we are running Spring 5 (Spring 5.3 in particular), which requires Tomcat 8.5+. However, it looks like OpenMRS has been running Spring 5 (Spring 5.1 up unyil 2.6) since Core 2.4, and we’ve been running Core 2.5 in production (with Tomcat 7) for a long time now.

Have we just gotten lucky, or am I missing something? (That being said, since Tomcat 7 is EOL, it does make sense to upgrade regardless).

@burke @dkayiwa @ibacher , fyi @mseaton

Take care, Mark

I don’t think PIH is alone in this. We migrated the community versions to Tomcat 8.5 with the release of 2.6.0, but the goal there wasn’t to make that the minimum supported version, just to demonstrate that OpenMRS could work on an actively maintained version of Tomcat (there are issues with 9 and 10 is a much further reach, because we’ll need to transition from javax.servlet to jakarta.servlet).

I guess we should add a prominent note to the release notes capturing this.

1 Like

Hmmm… @mseaton and I had been discussing yesterday migrating to Tomcat 9 (which I believe is what the SDK uses). Are you saying there are issues Tomcat 9 running OpenMRS 2.6.x? @ibacher

For reference when running OpenMRS 2.6.x we are seeing the following error hitting the REST Web Services endpoint:

ERROR - BaseRestController.handleException(124) |2023-08-10T15:32:52,008| Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: javax.servlet.http.HttpServletResponse.setContentLengthLong(J)V
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: javax.servlet.http.HttpServletResponse.setContentLengthLong(J)V
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1086) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.FrameworkServlet.doDelete(FrameworkServlet.java:931) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:653) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:728) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.openmrs.module.web.filter.ForcePasswordChangeFilter.doFilter(ForcePasswordChangeFilter.java:60) ~[legacyui.jar:?]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:73) ~[openmrs-web-2.6.1.jar:?]
	at org.openmrs.module.spa.filter.SpaFilter.doFilter(SpaFilter.java:53) ~[spa.jar:?]
	at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:71) ~[openmrs-web-2.6.1.jar:?]
	at org.openmrs.web.filter.GZIPFilter.doFilterInternal(GZIPFilter.java:65) ~[openmrs-web-2.6.1.jar:?]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:71) ~[openmrs-web-2.6.1.jar:?]
	at org.openmrs.module.webservices.rest.web.filter.AuthorizationFilter.doFilter(AuthorizationFilter.java:121) ~[webservices.rest-omod-common-2.40.0.jar:?]
	at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:71) ~[openmrs-web-2.6.1.jar:?]
	at org.openmrs.module.webservices.rest.web.filter.ContentTypeFilter.doFilter(ContentTypeFilter.java:64) ~[webservices.rest-omod-common-2.40.0.jar:?]
	at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:71) ~[openmrs-web-2.6.1.jar:?]
	at org.springframework.web.filter.ShallowEtagHeaderFilter.doFilterInternal(ShallowEtagHeaderFilter.java:106) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:71) ~[openmrs-web-2.6.1.jar:?]
	at org.openmrs.module.authentication.web.AuthenticationFilter.doFilter(AuthenticationFilter.java:154) ~[authentication.jar:?]
	at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:71) ~[openmrs-web-2.6.1.jar:?]
	at org.openmrs.module.owa.filter.OwaFilter.doFilter(OwaFilter.java:93) ~[owa.jar:?]
	at org.openmrs.module.owa.filter.OwaFilter.doFilter(OwaFilter.java:61) ~[owa.jar:?]
	at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:71) ~[openmrs-web-2.6.1.jar:?]
	at org.openmrs.module.pihcore.TermsAndConditionsFilter.doFilter(TermsAndConditionsFilter.java:93) ~[pihcore.jar:?]
	at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:71) ~[openmrs-web-2.6.1.jar:?]
	at org.openmrs.module.pihcore.RequestMonitoringFilter.doFilter(RequestMonitoringFilter.java:46) ~[pihcore.jar:?]
	at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:71) ~[openmrs-web-2.6.1.jar:?]
	at org.openmrs.module.web.filter.ModuleFilter.doFilter(ModuleFilter.java:57) ~[openmrs-web-2.6.1.jar:?]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.owasp.csrfguard.CsrfGuardFilter.handleSession(CsrfGuardFilter.java:107) ~[csrfguard-4.2.0.jar:4.2.0]
	at org.owasp.csrfguard.CsrfGuardFilter.doFilter(CsrfGuardFilter.java:97) ~[csrfguard-4.2.0.jar:4.2.0]
	at org.owasp.csrfguard.CsrfGuardFilter.doFilter(CsrfGuardFilter.java:68) ~[csrfguard-4.2.0.jar:4.2.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.openmrs.web.filter.OpenmrsFilter.doFilterInternal(OpenmrsFilter.java:114) ~[openmrs-web-2.6.1.jar:?]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.openmrs.web.filter.CookieClearingFilter.doFilterInternal(CookieClearingFilter.java:77) ~[openmrs-web-2.6.1.jar:?]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.springframework.orm.hibernate5.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:156) ~[spring-orm-5.3.23.jar:5.3.23]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:125) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.openmrs.web.filter.StartupFilter.doFilter(StartupFilter.java:111) ~[openmrs-web-2.6.1.jar:?]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.openmrs.web.filter.StartupFilter.doFilter(StartupFilter.java:111) ~[openmrs-web-2.6.1.jar:?]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.openmrs.web.filter.StartupFilter.doFilter(StartupFilter.java:111) ~[openmrs-web-2.6.1.jar:?]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:492) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:165) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:452) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1201) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:654) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:319) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_382]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_382]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-7.0.107.jar:7.0.107]
	at java.lang.Thread.run(Thread.java:750) ~[?:1.8.0_382]
Caused by: java.lang.NoSuchMethodError: javax.servlet.http.HttpServletResponse.setContentLengthLong(J)V
	at org.springframework.http.server.ServletServerHttpResponse.writeHeaders(ServletServerHttpResponse.java:130) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.http.server.ServletServerHttpResponse.getBody(ServletServerHttpResponse.java:96) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:126) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:44) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:227) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:293) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:183) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:78) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:135) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071) ~[spring-webmvc-5.3.23.jar:5.3.23]
	... 84 more

Googling the underlying error:

Caused by: java.lang.NoSuchMethodError: javax.servlet.http.HttpServletResponse.setContentLengthLong(J)V

Lead me to:

(See the first answer). What was interesting was that we’ve been running a version of Spring 5 since Core 2.4.x, I believe.

It seems valuable to update, especially since Tomcat 7 is EOL, but was hoping we could jump to Tomcat 9, since that seems to be what the SDK is using.

@mogoodrich which REST Web Services endpoint was it?

Good question @dkayiwa , I should have clarified.

It was the DELETE endpoint, both for deleting an obs and an encounter.

It occurs to me that I should confirm that this is actually working on OpenMRS 2.6.x when I’m using the SDK (which I believe runs Tomcat 9?). I will check this next week.

Take care, Mark

I think it was something related to the Tomcat 9 Docker image rather than Tomcat 9 itself. You’re right that that’s what the SDK runs and that seems to work.

Tracing back the exception there, it looks like it’s caused by this commit, which was first released in Spring 5.3.3. I think that although we used Spring 5 in 2.4 and 2.5, we didn’t use Spring 5.3 until 2.6.

Awesome, thanks for tracking that down @ibacher … but the bottom line is there’s nothing wrong with that commit, right? We’d just gotten lucky previously that we never hit anything previously that was added in the Servlet 3.1 container (which is a requirement of Spring 5, but which wasn’t provided in Tomcat until Tomcat 8).

Maybe I’m pushing it, but since we are running Tomcat 9 in the SDK, I’m inclined to see if we can upgrade the PIH EMR distro to run against Tomcat 9 for consistency (and just to limit the number of times we need to upgrade).

Anyone else running Tomcat9?

@raff were you having problems running with Tomcat9?

Thanks!

No. I mean, they’re pretty clear about what Spring 5 supports. We just probably didn’t hit a code-path that required the newer servlet APIs until then.

I’m fully on board with that!

1 Like