Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KAFKA-13328, KAFKA-13329 (1): Add preflight validations for key, value, and header converter classes #14304

Merged
merged 9 commits into from Dec 8, 2023

Conversation

C0urante
Copy link
Contributor

@C0urante C0urante commented Aug 28, 2023

Jira 1, Jira 2

Depends on #14303

Adds preflight validation checks for key, value, and header converter classes specified in connector configurations. For each converter type, these checks verify that:

  • The class has a public, no-args constructor
  • The class can be instantiated without an exception being thrown
  • The class is not abstract
    • In this case, a list of possible child classes is provided in a user-friendly error message
  • The class implements the expected interface

There are two components to the linked Jira tickets: this validation (which is fairly straightforward), and more sophisticated logic that leverages ConfigDef objects provided by these plugin classes. For ease of review, I'm planning on filing two PRs for these tickets, but instead of grouping by ticket (which would include all key/value converter changes in one ticket and all header converter changes in another), I think it might be easier to group by the kind of validation we're performing. If that's acceptable, the next PR will add ConfigDef-based validation for key, value, and header converters.

Committer Checklist (excluded from commit message)

  • Verify design and implementation
  • Verify test coverage and CI build status
  • Verify documentation (including upgrade notes)

* it would be the caller's responsibility to ensure that {@code task} was non-null before attempting to
* use a method reference from it.
*/
public static void maybeCloseQuietly(Object maybeCloseable, String name) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is overkill for a single use (which is the case in this PR), but a second use case is added for it in #14309.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found more use-cases for this today, so 👍 on adding this helper.

Copy link
Contributor

@yashmayya yashmayya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Chris, this is another really nice improvement! I just had a few comments and questions.

Copy link
Contributor

@gharris1727 gharris1727 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have all of the checks already, why didn't we add the validators! 🙃

Thanks for this, Chris.

String message = Utils.isBlank(childClassNames) ?
name + " is abstract and cannot be created." :
name + " is abstract and cannot be created. Did you mean " + childClassNames + "?";
throw new ConfigException(name, cls.getName(), message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first argument to this exception should be the config name, not the type name like it's used in the exception message and in the call-sites.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦 Thanks, good catch.

In pursuit of simplicity I've taken a stab at using the single-arg ConfigDef constructor, and using a one-size-fits-all error message that just says "This class" instead of, e.g., "Transform". Since all current uses for this code path involve (directly or indirectly) hitting a ConfigDef.Validator as part of config validation, I think the user-facing impact should only go as far as the small wording change. Let me know if you think it's better to preserve existing behavior, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this simplification, and it makes sense to generalize the error message as part of promoting this to the Utils class. parseForValidate will still attribute the error to the correct key/value.

name = "Class";
if (Modifier.isAbstract(cls.getModifiers())) {
String childClassNames = Stream.of(cls.getClasses())
.filter(cls::isAssignableFrom)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If AbstractClass.Key implemented the base interface, but didn't extend AbstractClass, this wouldn't show up in this listing. If this was baseClass::isAssignableFrom, it would find classes like that.

I don't know how often people would create Key and Value inner classes without subclassing the outer class, since that seems to be the reason people use the inner classes in the first place. But if you change this to take the baseClass and check baseClass.isAssignableFrom(cls) like both of the call-sites, you could change this filter statement too. I don't think this is a big deal, so feel free to leave this implementation as-is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a stab at something that would accept a base class and relax the filtering logic here accordingly, but it didn't quite feel right since we don't do verification that cls extends baseClass. I guess we could add that check as well and have something like Utils::ensureConcreteSubclass--how does that sound? I'm on the fence about it potentially doing too much for a utility method, but it does provide user-facing advantages.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could add that check as well and have something like Utils::ensureConcreteSubclass--how does that sound?

Yeah I imagine a ensureConcreteSubclass(Class<?> cls, Class<?> baseClass) that:

  1. asserts that cls is concrete and baseClass.isAssignableFrom(cls) is true
  2. if not valid, suggests child classes that are concrete andbaseClass::isAssignableFrom is true.

I don't think that it would compose well with the current ensureConcrete method though, so it would probably replace what is there already.

I'm on the fence about it potentially doing too much for a utility method, but it does provide user-facing advantages.

I thought that about the child class suggestion logic, but relented for the same reason. It significantly complicates this method which could otherwise be a single if condition, but that complication could help someone quickly resolve an obvious typo.

I don't think that asserting baseClass.isAssignableFrom(cls) is too much for this utility method, because we should always have a baseClass in mind when defining a CLASS configuration. Users of the AbstractConfig::getConfiguredInstance methods already need to provide a baseClass for subclass checking and type safety at runtime, this utility method would help users to remember the same check at validation time.

And worst-case, someone can just specify Object as the baseClass, and effectively disable the baseClass filtering.

@C0urante C0urante force-pushed the kafka-13328-13329-class-validators branch from 95a0ee8 to 8dfeca5 Compare October 31, 2023 16:43
@C0urante
Copy link
Contributor Author

@gharris1727 sorry for the delay, know it's been a minute! I've implemented your suggestions with the latest commit. This should be ready for another round whenever you have time.

Copy link
Contributor

@gharris1727 gharris1727 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@C0urante
Copy link
Contributor Author

C0urante commented Dec 8, 2023

Thanks Greg!

@C0urante C0urante merged commit dc857fb into apache:trunk Dec 8, 2023
1 check failed
@C0urante C0urante deleted the kafka-13328-13329-class-validators branch December 8, 2023 20:00
ex172000 pushed a commit to ex172000/kafka that referenced this pull request Dec 15, 2023
…e, and header converter classes (apache#14304)

Reviewers: Yash Mayya <yash.mayya@gmail.com>, Greg Harris <greg.harris@aiven.io>
gaurav-narula pushed a commit to gaurav-narula/kafka that referenced this pull request Jan 24, 2024
…e, and header converter classes (apache#14304)

Reviewers: Yash Mayya <yash.mayya@gmail.com>, Greg Harris <greg.harris@aiven.io>
anurag-harness pushed a commit to anurag-harness/kafka that referenced this pull request Feb 9, 2024
…e, and header converter classes (apache#14304)

Reviewers: Yash Mayya <yash.mayya@gmail.com>, Greg Harris <greg.harris@aiven.io>
anurag-harness added a commit to anurag-harness/kafka that referenced this pull request Feb 9, 2024
…e, and header converter classes (apache#14304) (#403)

Reviewers: Yash Mayya <yash.mayya@gmail.com>, Greg Harris <greg.harris@aiven.io>

Co-authored-by: Chris Egerton <chrise@aiven.io>
yyu1993 pushed a commit to yyu1993/kafka that referenced this pull request Feb 15, 2024
…e, and header converter classes (apache#14304)

Reviewers: Yash Mayya <yash.mayya@gmail.com>, Greg Harris <greg.harris@aiven.io>
AnatolyPopov pushed a commit to aiven/kafka that referenced this pull request Feb 16, 2024
…e, and header converter classes (apache#14304)

Reviewers: Yash Mayya <yash.mayya@gmail.com>, Greg Harris <greg.harris@aiven.io>
clolov pushed a commit to clolov/kafka that referenced this pull request Apr 5, 2024
…e, and header converter classes (apache#14304)

Reviewers: Yash Mayya <yash.mayya@gmail.com>, Greg Harris <greg.harris@aiven.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
3 participants