TOMEE-4642 - Don't fail deployment when a servlet/filter/listener class is missing - #2848
TOMEE-4642 - Don't fail deployment when a servlet/filter/listener class is missing#2848jungm wants to merge 4 commits into
Conversation
…ss is missing
When a war's web.xml or annotations name a servlet, filter or listener
class that is not packaged in the war, ProcessAnnotatedBeans.deploy
rethrew the ClassNotFoundException/NoClassDefFoundError as an
OpenEJBException, which aborted startup of the whole web context.
These loads only feed the annotation scanner, so a missing class simply
means there is nothing to scan - it must not bring down the context.
Jakarta Servlet 6.1 section 2.3.1 allows servlet loading to be delayed
"until the container determines the servlet is needed to service a
request", so an unresolved class is deferred, not fatal. The three paths
now log a warning and continue, matching the existing tolerant handling
of taglib listeners and the servlet name-fallback case.
Also fixed off-by-one MessageFormat placeholder indices ({1}{2}{3} ->
{0}{1}{2}) in the four related logger.debug calls, which dropped the
first argument.
Two Jakarta Servlet TCK deployments triggered this (RegistrationTests
naming filter AddFilterString, DefaultMappingTests naming servlet
TestServlet1).
|
The direction is right and the codebase already agrees with you — But the reported bug is not actually fixed on half the distributions:
Consequently the test can't do the job it's meant to do:
Other things:
|
WsDeployer is added to the deployer chain whenever openejb.webservices.enabled is set (default) and wsdl4j is available, which is the case on Plus, Plume and openejb-standalone. processPorts(WebModule) loads every servlet class *before* testing JaxWsUtils.isWebService(clazz), so it is not limited to webservice endpoints: a war naming a servlet class it does not package still failed to deploy on those distributions, with an identically worded error from a different class. Fixing AnnotationDeployer alone did not fix the reported bug. Resolve the class in its own try/catch that tolerates absence and skips webservice detection for that servlet. The previous catch(Exception) would not have caught NoClassDefFoundError anyway, since that is an Error - it propagated raw. The remaining catch now only covers genuine webservice configuration failures, so its message was corrected accordingly. Review feedback also addressed: - pass the cause and the module location to the log calls instead of discarding them, so a genuinely mis-packaged war is still diagnosable - filters and listeners log at error level rather than warning: unlike servlets, Servlet 6.1 does not allow their instantiation to be deferred, so a missing class there is very likely a real packaging error - correct the servlet comment to note that skipping the class also skips @Resource/@ejb processing for it - explain why the webservice handler-chain site stays fatal Add missingServletClassDoesNotFailFullConfiguration, which drives ConfigurationFactory.configureApplication so the whole deployer chain runs. The pre-existing test drove AnnotationDeployer directly and stayed green through the WsDeployer gap; the new one fails without the WsDeployer fix.
…essages
These logger.debug calls pass three arguments but start numbering their
placeholders at {1}, so the first argument - the class name that could not be
loaded, which is the useful part - was never rendered.
Covers the remaining occurrences after the ones touched by TOMEE-4642, so all
12 sites in this file are now consistent.
…ppBuilder Now that the deployer no longer rejects a web module naming a listener class the war does not package, that class name reaches LightweightWebAppBuilder, where loadClass threw a raw ClassNotFoundException in the middle of createApplication - worse than the OpenEJBException callers used to get. Skip the listener that cannot be loaded, logging it at error level, so the failure stays local to that listener as it does in the deployer.
|
Thanks — you were right that the bug wasn't actually fixed. Verified every claim before acting; all confirmed. Pushed 3 commits.
The test couldn't do its job. Correct — and demonstrated: added Discarded cause. Fixed — all sites now pass Filters/listeners vs §2.3.1. You're right that §2.3.1's deferral allowance covers servlets only. Both now log at
Fail-open side effect. Agreed; called it out explicitly in the servlet comment. MessageFormat. Did all of them — remaining 7 in a separate commit ( Handler chain. Left fatal, now with a comment saying why: a silently missing handler leaves the endpoint running under a weaker contract than declared (e.g. a dropped security handler), which is worse than disabling one component. One thing I did not do: removing the Unrelated pre-existing flakiness worth flagging: 🤖 Addressed by Claude Code |
What
When a war's
web.xmlor annotations name a servlet, filter or listener class that is not packaged in the war,AnnotationDeployer.ProcessAnnotatedBeans.deploy(WebModule)rethrew theClassNotFoundException/NoClassDefFoundErroras anOpenEJBException. That propagates out throughConfigurationFactory.configureApplicationand aborts startup of the whole web context.This changes the servlet, filter and listener paths to log a warning and continue instead of failing.
Why
These
classLoader.loadClass(...)calls exist only to feed the annotation scanner. A class that cannot be loaded simply contributes nothing to scan — it should not bring the context down.The old behaviour was also internally inconsistent: the taglib-listener loop right below, and the servlet name-fallback case, already only logged and continued. Only the explicit-class servlet/filter/listener paths were fatal.
Jakarta Servlet 6.1 §2.3.1 ("Loading and Instantiation") permits servlet loading to be "delayed until the container determines the servlet is needed to service a request", so deferring an unresolved class rather than failing eagerly at deploy time is spec-compliant. If such a component is actually used, Tomcat still surfaces the missing class per-component.
The
WsDeployerthrow for webservice servlet classes was intentionally left as-is, since a WS endpoint genuinely cannot be built without its class.Also
Fixed off-by-one
MessageFormatplaceholder indices ({1}{2}{3}→{0}{1}{2}) in the four relatedlogger.debugcalls, which were dropping the first argument from the message.Testing
Added
AnnotationDeployerTest.missingServletFilterAndListenerClassesDoNotFailDeployment, using the class names from the ticket (TestServlet1,AddFilterString, plus a missing listener). Verified it is a real regression test: reverting the servlet fix makes it fail with exactly the reported error (OpenEJBException: Unable to load servlet class: ...TestServlet1), and it passes with the fix.This is the TomEE-side fix for the two Jakarta Servlet TCK deployments that triggered the abort (
RegistrationTestsnaming filterAddFilterString;DefaultMappingTestsnaming servletTestServlet1). Removing the corresponding entries fromrunner-standalone/exclusions/servlet.txtin the apache/tomee-tck harness and confirming both classes pass is a follow-up in that separate repo.Jira: https://issues.apache.org/jira/browse/TOMEE-4642
🤖 Generated with Claude Code