You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Rewritten after a full sweep of the fork. The original report named two sites in Form.java; the root cause turns out to be systematic, so the scope below is the whole pattern. The javax-bound half is split out into a separate issue — see the end.
org.openidentityplatform.openam.click is a copy of Apache Click 2.3.0 taken in-tree so the framework could be moved from javax.servlet to jakarta.servlet. During that copy, imports were repointed at the fork class by class, and only about half were done.
Measured over the 57 files of the fork:
count
import org.apache.click.* statements
60
— used in code
40
— appearing only in javadoc (@see, {@link})
20
distinct upstream classes imported
32
— a class of the same name already exists in the fork
16
— no fork equivalent exists
16
util/ContainerUtils.java:38-49 shows the pattern in a single import block: Control, Container, Field and Form are the fork's, while Button, Label, FieldSet, LogService, ClickUtils and HtmlStringBuffer are still upstream.
Why the mixed imports break behaviour
The fork's Button, Label and ActionLink extend the fork's Field → AbstractControl → Control. They have no relationship to the upstream classes of the same name. Container.getControls() returns List<Control> of the forkControl, and add(Control) accepts only that type, so an upstream control can never be inside a fork container.
Every instanceof against an upstream type over that collection is therefore a constant false, and every guard built on one silently does nothing.
Filters that never fire
Site
Check
Consequence
ContainerUtils:1179addInputFields()
instanceof Label || instanceof Button
buttons and labels are not filtered out and are returned by getInputFields(), so they reach Form.validate() (Form:1141, :1357), copyContainerToObject() and getFieldMap() as if they were input fields
ContainerUtils:1209addHiddenFields()
same
same, for getHiddenFields() (Form:2447)
ContainerUtils:1246addFieldsAndLabels()
instanceof Button
same
ContainerUtils:1303addErrorFields()
instanceof Button
same
ContainerUtils:1130, :1136addButtons()
instanceof Button
getButtons() always returns an empty list
ContainerUtils:553insert()
!(control instanceof Label)
the exemption that lets several labels share a name never applies, so insert() throws IllegalArgumentException where upstream allowed the add
ClickUtils:1454-1455getCssSelector()
instanceof ActionLink
a fork ActionLink takes the else arm and gets a tag[name=…] selector, although the comment right above explains that links do not render a name attribute — the selector cannot match
The same logic in Form.addStatefulFields:3051 — control instanceof Label || control instanceof Button — is correct, because Form.java does not import the upstream Label/Button and they resolve inside the fork's own package. Two files carrying the same code behave differently; that is the signature of the half-finished repoint.
Branches that can never be taken
Form:3061 — control instanceof org.apache.click.control.Container, while the cast on the next line is to the forkContainer. The recursion is dead, so addStatefulFields() does not keep the promise in its javadoc to "recursively include any Fields contained in child containers", and getState()/setState() (Form:1887, :1915) skip every field nested in a child container. Had the condition ever matched, the cast would throw ClassCastException.
Form:2526-2527 (renderControls()), Field:315, :747 (isDisabled(), isReadonly()), ContainerUtilsaddInputFields/addHiddenFields — all test instanceof FieldSet against the upstream class. The fork ships no FieldSet at all, so the <td> styling and the parent-walk never run.
Reachability
None of this is a live defect today. No Click Form is constructed anywhere in the product — the only HiddenField instances are the framework's own NonProcessedHiddenField inside Form.java itself — and the pages the fork's servlet serves (the configurator and upgrader) create no controls. See #1109 for the full argument. The configurator works because it exercises the one path that was repointed completely.
Suggested scope
Repoint the 16 upstream imports that already have a fork class of the same name: ActionLink, Button, Label, Decorator, AbstractContainer, ClickUtils, HtmlStringBuffer, ConfigService, LogService, FileUploadService, ClickResourceService, VelocityTemplateService, DefaultMessagesMapService, CssStyle, JsImport, JsScript. This is mechanical and fixes every row of the table above.
Decide what to do about FieldSet: add a fork class or delete the branches and the import.
Fix Form:3061 to name the fork Container, matching the cast below it.
Drop the 20 javadoc-only imports that point at classes the fork does not ship (Submit, Reset, Checkbox, Panel, …), or repoint the ones that do have an equivalent.
Regression cover: getInputFields() and getButtons() over a container holding a fork Button and Label give a directly assertable result. There is currently no test anywhere under openam-core/src/test/java/org/openidentityplatform/openam/click/ except the one added by Remove the Java serialization round trip from the bundled Click HiddenField #1109.
Closing step: with the imports repointed, drop org.apache.click:click-nodeps:2.3.0 from openam-core. It cannot be removed before then — the fork imports 32 classes from that jar and does not compile without it.
Out of scope here
The other 16 upstream classes have no fork equivalent, and three of them (Format, MessagesMap, and the calls to org.apache.click.util.ClickUtils.getLogService()) reach upstream code that is bound to javax.servlet, which is not on the classpath. That is a different failure mode — NoClassDefFoundError rather than a branch quietly not taken — and needs its own fix. Tracked separately.
org.openidentityplatform.openam.clickis a copy of Apache Click 2.3.0 taken in-tree so the framework could be moved fromjavax.servlettojakarta.servlet. During that copy, imports were repointed at the fork class by class, and only about half were done.Measured over the 57 files of the fork:
import org.apache.click.*statements@see,{@link})util/ContainerUtils.java:38-49shows the pattern in a single import block:Control,Container,FieldandFormare the fork's, whileButton,Label,FieldSet,LogService,ClickUtilsandHtmlStringBufferare still upstream.Why the mixed imports break behaviour
The fork's
Button,LabelandActionLinkextend the fork'sField→AbstractControl→Control. They have no relationship to the upstream classes of the same name.Container.getControls()returnsList<Control>of the forkControl, andadd(Control)accepts only that type, so an upstream control can never be inside a fork container.Every
instanceofagainst an upstream type over that collection is therefore a constantfalse, and every guard built on one silently does nothing.Filters that never fire
ContainerUtils:1179addInputFields()instanceof Label || instanceof ButtongetInputFields(), so they reachForm.validate()(Form:1141,:1357),copyContainerToObject()andgetFieldMap()as if they were input fieldsContainerUtils:1209addHiddenFields()getHiddenFields()(Form:2447)ContainerUtils:1246addFieldsAndLabels()instanceof ButtonContainerUtils:1303addErrorFields()instanceof ButtonContainerUtils:1130,:1136addButtons()instanceof ButtongetButtons()always returns an empty listContainerUtils:553insert()!(control instanceof Label)insert()throwsIllegalArgumentExceptionwhere upstream allowed the addClickUtils:1454-1455getCssSelector()instanceof ActionLinkActionLinktakes theelsearm and gets atag[name=…]selector, although the comment right above explains that links do not render anameattribute — the selector cannot matchThe same logic in
Form.addStatefulFields:3051—control instanceof Label || control instanceof Button— is correct, becauseForm.javadoes not import the upstreamLabel/Buttonand they resolve inside the fork's own package. Two files carrying the same code behave differently; that is the signature of the half-finished repoint.Branches that can never be taken
Form:3061—control instanceof org.apache.click.control.Container, while the cast on the next line is to the forkContainer. The recursion is dead, soaddStatefulFields()does not keep the promise in its javadoc to "recursively include any Fields contained in child containers", andgetState()/setState()(Form:1887,:1915) skip every field nested in a child container. Had the condition ever matched, the cast would throwClassCastException.Form:2526-2527(renderControls()),Field:315,:747(isDisabled(),isReadonly()),ContainerUtilsaddInputFields/addHiddenFields— all testinstanceof FieldSetagainst the upstream class. The fork ships noFieldSetat all, so the<td>styling and the parent-walk never run.Reachability
None of this is a live defect today. No Click
Formis constructed anywhere in the product — the onlyHiddenFieldinstances are the framework's ownNonProcessedHiddenFieldinsideForm.javaitself — and the pages the fork's servlet serves (the configurator and upgrader) create no controls. See #1109 for the full argument. The configurator works because it exercises the one path that was repointed completely.Suggested scope
ActionLink,Button,Label,Decorator,AbstractContainer,ClickUtils,HtmlStringBuffer,ConfigService,LogService,FileUploadService,ClickResourceService,VelocityTemplateService,DefaultMessagesMapService,CssStyle,JsImport,JsScript. This is mechanical and fixes every row of the table above.FieldSet: add a fork class or delete the branches and the import.Form:3061to name the forkContainer, matching the cast below it.Submit,Reset,Checkbox,Panel, …), or repoint the ones that do have an equivalent.getInputFields()andgetButtons()over a container holding a forkButtonandLabelgive a directly assertable result. There is currently no test anywhere underopenam-core/src/test/java/org/openidentityplatform/openam/click/except the one added by Remove the Java serialization round trip from the bundled Click HiddenField #1109.org.apache.click:click-nodeps:2.3.0fromopenam-core. It cannot be removed before then — the fork imports 32 classes from that jar and does not compile without it.Out of scope here
The other 16 upstream classes have no fork equivalent, and three of them (
Format,MessagesMap, and the calls toorg.apache.click.util.ClickUtils.getLogService()) reach upstream code that is bound tojavax.servlet, which is not on the classpath. That is a different failure mode —NoClassDefFoundErrorrather than a branch quietly not taken — and needs its own fix. Tracked separately.