Skip to content

Click fork: upstream Format, MessagesMap and ClickUtils.getLogService() are javax.servlet-bound and fail with NoClassDefFoundError #1111

Description

@vharseko

Split out of #1110, which covers the upstream imports that already have a fork class of the same name. This one is about the upstream classes that have no fork equivalent and that reach javax.servlet-bound code. The failure mode is different: not a branch that quietly does nothing, but NoClassDefFoundError at the first call.

javax.servlet is not on the classpath

org.openidentityplatform.openam.click exists so the framework could run under Jakarta. The deployment resolves only jakarta.servlet-api-5.0.0; none of the 381 jars on the openam-server-only classpath carries javax/servlet/http/HttpServlet.class, and there is no javax.servlet-api anywhere in either module.

Upstream Click 2.3.0 is compiled against javax.servlet. Disassembling click-nodeps-2.3.0.jar:

org.apache.click.ClickServlet extends javax.servlet.http.HttpServlet

ClickUtils.getLogService():
    org/apache/click/Context.getThreadLocalContext()
    org/apache/click/Context.getServletContext()  ->  javax/servlet/ServletContext
    org/apache/click/util/ClickUtils.getConfigService(javax/servlet/ServletContext)

So any entry into upstream Click's Context-dependent code fails twice over: the upstream Context keeps its own thread local, which nothing populates because only the fork's servlet is mapped; and the javax.servlet types it hands back do not exist at runtime.

Where the fork calls into it

ClickUtils.getLogService() — 7 call sites

util/ContainerUtils.java:117, :140, :238, :264 (copyContainerToObject(), copyObjectToContainer()), :1053, :1089 (copyFieldsToMap(), copyMapToFields()), and util/ClickUtils.java:530 in the fork's own ClickUtils.

All of them call org.apache.click.util.ClickUtils.getLogService().

The fix is local and the fork already uses the correct pattern two places away — util/ClickUtils.java:1540:

LogService logger = getConfigService(servletContext).getLogService();

and :907:

LogService logger = configService.getLogService();

org.apache.click.util.Format

Page.java:170 declares protected Format format, with getFormat()/setFormat() at :484/:493; service/XmlConfigService.java:178 holds Class<? extends Format> formatClass and :386 createFormat() instantiates it, defaulting to org.apache.click.util.Format at :1524.

A Format instance is created per page and exposed to templates as $format. Its getLocale(), url() and date() all go through Context.getThreadLocalContext().

org.apache.click.util.MessagesMap

service/DefaultMessagesMapService.java:65 returns new MessagesMap(baseClass, globalResource, locale) — the upstream class. ClickServlet.java:1600 calls page.getMessages() on every request, so one of these is constructed per request.

Construction survives only by accident: the three-argument constructor does not touch Context, while ensureInitialized() — which does Context.getThreadLocalContext().getServletContext() — runs on the first get().

How close this is to firing

Nothing triggers it today, and the margin is thin.

  • No shipped .htm template dereferences $format or $messages, so MessagesMap.ensureInitialized() and every Format method stay uncalled. A single $messages.someKey in any configurator template would turn every request into a hard failure.
  • The ContainerUtils copy methods are only reachable from a Click Form, and no Form is constructed anywhere in the product (see Remove the Java serialization round trip from the bundled Click HiddenField #1109).
  • ErrorPage.onInit() does build an ErrorReport on every error, but click.xml runs in <mode value="production"/> and the shipped click/error.htm is a static page that never dereferences $errorReport.

Suggested fix

  1. Replace the 7 org.apache.click.util.ClickUtils.getLogService() calls with the fork's own getConfigService(...).getLogService(). Cheapest change, removes the sharpest edge.
  2. Fork org.apache.click.util.Format and org.apache.click.util.MessagesMap, or drop them and use fork-local equivalents. Both are small and neither needs javax.servlet other than through Context, which the fork already has.
  3. Audit the remaining upstream classes with no fork twin for the same dependency. PropertyUtils, RequestTypeConverter and HtmlStringBuffer were checked and carry no reference to Context, ConfigService or javax.servlet, so they are safe to keep using as-is.

Removing org.apache.click:click-nodeps:2.3.0 altogether is the end state, tracked as the closing step of #1110.

Metadata

Metadata

Assignees

Labels

bugjavaPull requests that update java coderefactoringCode cleanup, refactor, dead-code or dependency removal

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions