Skip to content

Introduce ValidateMessage to enable alternative protobuf runtimes#480

Open
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure
Open

Introduce ValidateMessage to enable alternative protobuf runtimes#480
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure

Conversation

@andrewparmet

@andrewparmet andrewparmet commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Adds a runtime-independent ValidateMessage interface so alternative protobuf runtimes (for example, protokt) can be validated without first converting their messages to com.google.protobuf.Message.

Changes

  • Add the public ValidateMessage and Value surface: getDescriptorForType, hasField, getField, celValue, plus rawValue / jvmValue for the value side.
  • Add ProtobufValidateMessage, which adapts com.google.protobuf.Message to ValidateMessage, so the existing Message validation path is unchanged.
  • Route the evaluators (FieldEvaluator, OneofEvaluator, MessageOneofEvaluator, AnyEvaluator, MapEvaluator, list/object values, WrappedValueEvaluator) through ValidateMessage instead of Message directly.
  • Expose Validator.validate(ValidateMessage) as the entry point for alternative-runtime callers; the Message overload delegates through it.

Related work

Testing

./gradlew :conformance:conformance passes.

@andrewparmet andrewparmet marked this pull request as ready for review June 14, 2026 15:22
pkwarren pushed a commit that referenced this pull request Jun 15, 2026
Moves protovalidate-java's CEL setup from the deprecated standard
runtime to the planner runtime.

## Notable callouts

- Wire the compiler and runtime by hand rather than through
`plannerCelBuilder()`. `CelRuntimeImpl` directs callers to subset the
standard library via `setStandardFunctions` rather than
`setStandardEnvironmentEnabled`, so the runtime drops stdlib `matches`
that way while the checker continues to use
`setStandardEnvironmentEnabled(false)`. This keeps `CustomOverload`'s
caching `matches` replacement (cel-java#1038).
- Set
`CelOptions.current().enableHeterogeneousNumericComparisons(true)`,
which `CelRuntimeImpl` requires.
- Update `cel-java` to 0.13.1 preparing to support alternative Protobuf
runtimes.

## Related work:

- cel-java: 0.13.1 carries the CelValue-path fixes this work relies on
(fixed32/64 treated as unsigned; FieldMask navigable as a message,
cel-expr/cel-java#1074).
- protovalidate-java: #480 introduces `MessageReflector` to support
alternative Protobuf runtimes, which builds on this runtime change
(supersedes #202).
- protokt: open-toast/protokt#402 is the downstream consumer that
validates protokt messages through `MessageReflector` on this planner
runtime.

## Testing

`./gradlew :conformance:conformance` passes.
@jonbodner-buf

Copy link
Copy Markdown
Contributor

@andrewparmet can you update the branch from main?

@andrewparmet

Copy link
Copy Markdown
Contributor Author

@jonbodner-buf done!

@jonbodner-buf

Copy link
Copy Markdown
Contributor

My biggest concern is there are no tests to validate these changes. Yes, the existing tests still pass, but nothing in the existing tests calls public ValidationResult validate(MessageReflector message) throws ValidationException .

The exception thrown by build.buf.protovalidate.MessageValue#jvmValue and build.buf.protovalidate.MessageReflectorValue#jvmValue also concern me, want to make sure that there is no code path where that could be triggered, especially for MessageReflectorValue, since it will be used with external types that implement MessageReflector.

andrewparmet and others added 2 commits June 17, 2026 23:15
Rename the MessageReflector interface to ValidateMessage, and accordingly
ProtobufMessageReflector -> ProtobufValidateMessage and MessageReflectorValue ->
ValidateMessageValue (with its `reflector` field/locals renamed to `message`).

Also give the jvmValue UnsupportedOperationException on the message-backed Value
types a descriptive message: MessageValue notes it carries the rule message and
should be read via messageValue()/celValue(); the ValidateMessage-backed value
notes that a getField() implementation returned a message value for a field
evaluated as a scalar.
Add ValidateMessageTest, which validates through a map-backed ValidateMessage
(no com.google.protobuf.Message): scalar / nested / repeated / map /
repeated-message / map-message violations and their field paths, a valid
message, the well-known Timestamp type, bytes, enum, unsigned (uint32), the
Int32Value wrapper, a required oneof, the clear error when a message field is
not backed by a ValidateMessage, and that jvmValue is unsupported on the
message-backed Value types.

Co-authored-by: Jon Bodner <jbodner@buf.build>
@andrewparmet andrewparmet changed the title Introduce MessageReflector to enable alternative protobuf runtimes Introduce ValidateMessage to enable alternative protobuf runtimes Jun 18, 2026
@andrewparmet

Copy link
Copy Markdown
Contributor Author

Pushed up some tests - thanks for the starter patch from your investigation! The existing Validator tests do indirectly call validate(ValidateMessage message) via the Message overload, which hands off directly to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants