Repository: victools/jsonschema-generator
Pull request #344 (2023-04-19 22:42:51)

feat: Support inheritance for validation constraint annotations
Resolving #297 and replacing #299.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df7e0e2..6d7032b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+### `jsonschema-generator`
+#### Added
+- extend `TypeContext` creation to support configuration of differing annotation inheritance by annotation type
+
+### `jsonschema-module-jakarta-validation`
+#### Fixed
+- consider inheritance of validation constraint annotations
+- when limiting scope to validation groups, also consider a specified group's supertypes 
+
+### `jsonschema-module-javax-validation`
+#### Fixed
+- consider inheritance of validation constraint annotations
+- when limiting scope to validation groups, also consider a specified group's supertypes
 
 ## [4.30.0] - 2023-04-16
 ### `jsonschema-generator`
diff --git a/jsonschema-examples/src/main/java/com/github/victools/jsonschema/examples/AnnotationInheritanceExample.java b/jsonschema-examples/src/main/java/com/github/victools/jsonschema/examples/AnnotationInheritanceExample.java
index becb7fb..8517f7b 100644
--- a/jsonschema-examples/src/main/java/com/github/victools/jsonschema/examples/AnnotationInheritanceExample.java
+++ b/jsonschema-examples/src/main/java/com/github/victools/jsonschema/examples/AnnotationInheritanceExample.java
@@ -32,8 +32,8 @@ import java.lang.annotation.Inherited;
 
 /**
  * Example created in response to <a href="https://github.com/victools/jsonschema-generator/discussions/333">#333</a>.
- * <br/>
- * Ensuring "type" inclusion on "allOf" parts holding collected attributes, e.g., "additionalProperties".
+ * <br>
+ * Adapted after inheritance is supported out-of-the-box.
  */
 public class AnnotationInheritanceExample implements SchemaGenerationExampleInterface {
 
@@ -42,11 +42,9 @@ public class AnnotationInheritanceExample implements SchemaGenerationExampleInte
         SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON)
                 .with(new JakartaValidationModule());
         SchemaGeneratorConfig config = configBuilder.build();
-        // the default AnnotationInclusion.INCLUDE_AND_INHERIT_IF_INHERITED requires @Inherited on annotations being looked up
-        // using AnnotationInclusion.INCLUDE_AND_INHERIT instead considers all annotations on overridden methods as well
-        SchemaGenerator generator = new SchemaGenerator(config,
-                TypeContextFactory.createTypeContext(AnnotationInclusion.INCLUDE_AND_INHERIT, config));
-        return generator.generateSchema(Book.class);
+        // jakarta (and javax) validation constraints annotations are always inherited (INCLUDE_AND_INHERIT)
+        // there are overrides being applied through the modules (INCLUDE_AND_INHERIT_IF_INHERITED)
+        return new SchemaGenerator(config).generateSchema(Book.class);
     }
 
     static class Book implements Publication {
diff --git a/jsonschema-generator-parent/pom.xml b/jsonschema-generator-parent/pom.xml
index cacb900..5b4125b 100644
--- a/jsonschema-generator-parent/pom.xml
+++ b/jsonschema-generator-parent/pom.xml
@@ -60,6 +60,7 @@
             <roles>
                 <role>Provided PRs #116 and #118 (part of initial Swagger2Module)</role>
                 <role>Provided implementation for #204 (readOnly/writeOnly in JacksonModule)</role>
+                <role>Provided PR #299 and feedback on final implementation (inheritance of validation constraint annotations)</role>
             </roles>
         </contributor>
         <contributor>
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
index 8c5f964..2795744 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
@@ -16,12 +16,14 @@
 
 package com.github.victools.jsonschema.generator;
 
+import com.fasterxml.classmate.AnnotationInclusion;
 import com.fasterxml.classmate.ResolvedType;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.github.victools.jsonschema.generator.naming.SchemaDefinitionNamingStrategy;
+import java.lang.annotation.Annotation;
 import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.List;
@@ -182,6 +184,15 @@ public interface SchemaGeneratorConfig extends StatefulConfig {
      */
     ArrayNode createArrayNode();
 
+    /**
+     * Provide the explicit rule overrides of when to include particular annotations (e.g., with or without {@code @Inherited}).
+     *
+     * @return annotation inclusion rules for specific annotation types
+     *
+     * @since 4.31.0
+     */
+    Map<Class<? extends Annotation>, AnnotationInclusion> getAnnotationInclusionOverrides();
+
     /**
      * Implementation of the {@link java.util.Comparator#compare(Object, Object)} interface method to determine the order of fields and methods in an
      * object's {@code "properties"}.
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
index ccf9c06..90e5610 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
@@ -16,10 +16,12 @@
 
 package com.github.victools.jsonschema.generator;
 
+import com.fasterxml.classmate.AnnotationInclusion;
 import com.fasterxml.jackson.core.json.JsonWriteFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import com.github.victools.jsonschema.generator.impl.SchemaGeneratorConfigImpl;
+import java.lang.annotation.Annotation;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -57,6 +59,7 @@ public class SchemaGeneratorConfigBuilder {
     private final SchemaGeneratorGeneralConfigPart typesInGeneralConfigPart = new SchemaGeneratorGeneralConfigPart();
     private final SchemaGeneratorConfigPart<FieldScope> fieldConfigPart = new SchemaGeneratorConfigPart<>();
     private final SchemaGeneratorConfigPart<MethodScope> methodConfigPart = new SchemaGeneratorConfigPart<>();
+    private final Map<Class<? extends Annotation>, AnnotationInclusion> annotationInclusionOverrides = new LinkedHashMap<>();
 
     /**
      * Constructor of an empty configuration builder for a {@link SchemaVersion#DRAFT_7 Draft 7} schema. This is equivalent to calling:<br>
@@ -157,7 +160,8 @@ public class SchemaGeneratorConfigBuilder {
                 enabledOptions,
                 this.typesInGeneralConfigPart,
                 this.fieldConfigPart,
-                this.methodConfigPart);
+                this.methodConfigPart,
+                this.annotationInclusionOverrides);
     }
 
     /**
@@ -288,4 +292,18 @@ public class SchemaGeneratorConfigBuilder {
         }
         return this;
     }
+
+    /**
+     * Register an explicit annotation inclusion rule for a given annotation type.
+     *
+     * @param annotationType type of annotation for which the inclusion behaviour should be overridden
+     * @param override inclusion behaviour to apply for the given annotation type
+     * @return this builder instance (for chaining)
+     *
+     * @since 4.31.0
+     */
+    public SchemaGeneratorConfigBuilder withAnnotationInclusionOverride(Class<? extends Annotation> annotationType, AnnotationInclusion override) {
+        this.annotationInclusionOverrides.put(annotationType, override);
+        return this;
+    }
 }
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
index 0f9b360..c8ffebb 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
@@ -51,19 +51,29 @@ public class TypeContext {
      * Constructor.
      *
      * @param annotationConfig annotation configuration to apply when collecting resolved fields and methods
+     *
+     * @deprecated use {@link #TypeContext(AnnotationConfiguration, SchemaGeneratorConfig)} instead
      */
+    @Deprecated
     public TypeContext(AnnotationConfiguration annotationConfig) {
         this(annotationConfig, false);
     }
 
     /**
      * Constructor.
+     * <br>
+     * Note: when providing an instance of {@link AnnotationConfiguration.StdConfiguration} as first parameter, any configured annotation inclusion
+     * overrides are applied automatically.
      *
      * @param annotationConfig annotation configuration to apply when collecting resolved fields and methods
      * @param generatorConfig generator configuration indicating whether argument free methods should be represented as fields
      */
     public TypeContext(AnnotationConfiguration annotationConfig, SchemaGeneratorConfig generatorConfig) {
         this(annotationConfig, generatorConfig.shouldDeriveFieldsFromArgumentFreeMethods());
+        if (annotationConfig instanceof AnnotationConfiguration.StdConfiguration) {
+            generatorConfig.getAnnotationInclusionOverrides()
+                    .forEach(((AnnotationConfiguration.StdConfiguration) annotationConfig)::setInclusion);
+        }
     }
 
     /**
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/DefinitionKey.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/DefinitionKey.java
index 109ffda..531e1db 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/DefinitionKey.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/DefinitionKey.java
@@ -49,7 +49,7 @@ public class DefinitionKey {
     }
 
     /**
-     * Getter for the a custom definition provider that was the first to be skipped during the generation of the schema definition. Ignoring a custom
+     * Getter for the custom definition provider that was the first to be skipped during the generation of the schema definition. Ignoring a custom
      * definition provider allows for the next custom definition provider to be applied, or if there is none or no custom definition is returned:
      * falling-back on the standard definition for the targeted type.
      *
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/PropertySortUtils.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/PropertySortUtils.java
index db477c4..7d310d6 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/PropertySortUtils.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/PropertySortUtils.java
@@ -22,7 +22,8 @@ import java.util.Comparator;
 /**
  * Utility class containing the declaration of the default property sort algorithm and its components.
  */
-public class PropertySortUtils {
+public enum PropertySortUtils {
+    /* using enum type to avoid instantiation */;
 
     /**
      * {@link Comparator} sorting properties: with fields before methods.
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
index 9998b78..b109dc9 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
@@ -16,6 +16,7 @@
 
 package com.github.victools.jsonschema.generator.impl;
 
+import com.fasterxml.classmate.AnnotationInclusion;
 import com.fasterxml.classmate.ResolvedType;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -39,6 +40,7 @@ import com.github.victools.jsonschema.generator.SchemaVersion;
 import com.github.victools.jsonschema.generator.TypeAttributeOverrideV2;
 import com.github.victools.jsonschema.generator.TypeScope;
 import com.github.victools.jsonschema.generator.naming.SchemaDefinitionNamingStrategy;
+import java.lang.annotation.Annotation;
 import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.Collections;
@@ -59,29 +61,29 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
     private final SchemaGeneratorGeneralConfigPart typesInGeneralConfigPart;
     private final SchemaGeneratorConfigPart<FieldScope> fieldConfigPart;
     private final SchemaGeneratorConfigPart<MethodScope> methodConfigPart;
+    private final Map<Class<? extends Annotation>, AnnotationInclusion> annotationInclusionOverrides;
 
     /**
      * Constructor of a configuration instance.
      *
-     * @param objectMapper supplier for object and array nodes for the JSON structure being generated
-     * @param schemaVersion designated JSON Schema version
-     * @param enabledOptions enabled settings/options (either by default or explicitly set)
+     * @param objectMapper             supplier for object and array nodes for the JSON structure being generated
+     * @param schemaVersion            designated JSON Schema version
+     * @param enabledOptions           enabled settings/options (either by default or explicitly set)
      * @param typesInGeneralConfigPart configuration part for context-independent attribute collection
-     * @param fieldConfigPart configuration part for fields
-     * @param methodConfigPart configuration part for methods
+     * @param fieldConfigPart          configuration part for fields
+     * @param methodConfigPart         configuration part for methods
+     * @param inclusionOverrides       overrides of the rules when to include certain annotations (e.g., with or without {@code @Inherited} annotation
      */
-    public SchemaGeneratorConfigImpl(ObjectMapper objectMapper,
-            SchemaVersion schemaVersion,
-            Set<Option> enabledOptions,
-            SchemaGeneratorGeneralConfigPart typesInGeneralConfigPart,
-            SchemaGeneratorConfigPart<FieldScope> fieldConfigPart,
-            SchemaGeneratorConfigPart<MethodScope> methodConfigPart) {
+    public SchemaGeneratorConfigImpl(ObjectMapper objectMapper, SchemaVersion schemaVersion, Set<Option> enabledOptions,
+            SchemaGeneratorGeneralConfigPart typesInGeneralConfigPart, SchemaGeneratorConfigPart<FieldScope> fieldConfigPart,
+            SchemaGeneratorConfigPart<MethodScope> methodConfigPart, Map<Class<? extends Annotation>, AnnotationInclusion> inclusionOverrides) {
         this.objectMapper = objectMapper;
         this.schemaVersion = schemaVersion;
         this.enabledOptions = enabledOptions;
         this.typesInGeneralConfigPart = typesInGeneralConfigPart;
         this.fieldConfigPart = fieldConfigPart;
         this.methodConfigPart = methodConfigPart;
+        this.annotationInclusionOverrides = inclusionOverrides;
     }
 
     @Override
@@ -196,6 +198,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.getObjectMapper().createArrayNode();
     }
 
+    @Override
+    public Map<Class<? extends Annotation>, AnnotationInclusion> getAnnotationInclusionOverrides() {
+        return Collections.unmodifiableMap(this.annotationInclusionOverrides);
+    }
+
     @Override
     public int sortProperties(MemberScope<?, ?> first, MemberScope<?, ?> second) {
         return this.typesInGeneralConfigPart.getPropertySorter().compare(first, second);
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/TypeContextFactory.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/TypeContextFactory.java
index 0cb9a37..9e820ae 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/TypeContextFactory.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/TypeContextFactory.java
@@ -24,7 +24,8 @@ import com.github.victools.jsonschema.generator.TypeContext;
 /**
  * Factory class for creating {@link TypeContext} instances.
  */
-public class TypeContextFactory {
+public enum TypeContextFactory {
+    /* using enum type to avoid instantiation */;
 
     /**
      * Create the default {@link TypeContext} with {@link AnnotationInclusion#INCLUDE_AND_INHERIT_IF_INHERITED}.
@@ -32,7 +33,9 @@ public class TypeContextFactory {
      * This is equivalent to calling: {@code TypeContextFactory.createTypeContext(AnnotationInclusion.INCLUDE_AND_INHERIT_IF_INHERITED)}
      *
      * @return created {@link TypeContext} instance
+     * @deprecated use {@link #createDefaultTypeContext(SchemaGeneratorConfig)} instead
      */
+    @Deprecated
     public static TypeContext createDefaultTypeContext() {
         return TypeContextFactory.createTypeContext(AnnotationInclusion.INCLUDE_AND_INHERIT_IF_INHERITED);
     }
@@ -55,7 +58,9 @@ public class TypeContextFactory {
      *
      * @param annotationInclusion indication which annotations to include during type resolution/introspection
      * @return created {@link TypeContext} instance
+     * @deprecated use {@link #createTypeContext(AnnotationInclusion, SchemaGeneratorConfig)} instead
      */
+    @Deprecated
     public static TypeContext createTypeContext(AnnotationInclusion annotationInclusion) {
         return TypeContextFactory.createTypeContext(new AnnotationConfiguration.StdConfiguration(annotationInclusion));
     }
@@ -78,13 +83,18 @@ public class TypeContextFactory {
      *
      * @param annotationConfig configuration determining which annotations to include during type resolution/introspection
      * @return created {@link TypeContext} instance
+     * @deprecated use {@link #createTypeContext(AnnotationConfiguration, SchemaGeneratorConfig)} instead
      */
+    @Deprecated
     public static TypeContext createTypeContext(AnnotationConfiguration annotationConfig) {
         return new TypeContext(annotationConfig);
     }
 
     /**
      * Create the a {@link TypeContext} with the given {@link AnnotationConfiguration}.
+     * <br>
+     * Note: when providing an instance of {@link AnnotationConfiguration.StdConfiguration} as first parameter, any configured annotation inclusion
+     * overrides are applied automatically.
      *
      * @param annotationConfig configuration determining which annotations to include during type resolution/introspection
      * @param config configuration to consider
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/AdditionalPropertiesModule.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/AdditionalPropertiesModule.java
index de64e7e..0467373 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/AdditionalPropertiesModule.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/AdditionalPropertiesModule.java
@@ -103,8 +103,8 @@ public class AdditionalPropertiesModule implements Module {
      * Constructor.
      *
      * @param generalAdditionalPropertiesResolver resolver for additionalProperties of types in general
-     * @param fieldAdditionalPropertiesResolver resolver for additionalPropoerties for fields
-     * @param methodAdditionalPropertiesResolver resolver for additionalPropoerties for methods
+     * @param fieldAdditionalPropertiesResolver resolver for additionalProperties for fields
+     * @param methodAdditionalPropertiesResolver resolver for additionalProperties for methods
      */
     public AdditionalPropertiesModule(ConfigFunction<TypeScope, Type> generalAdditionalPropertiesResolver,
             BiFunction<FieldScope, SchemaGenerationContext, JsonNode> fieldAdditionalPropertiesResolver,
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java
index 5ccc841..3054aa7 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java
@@ -56,7 +56,7 @@ public class EnumModule implements Module {
 
     /**
      * Factory method: creating an {@link EnumModule} instance that treats all enums as objects but hides all methods declared by the general enum
-     * interface but {@link Enum#name() name()}. Methods and fields (including the enum constants) declared by their sub types are not excluded.
+     * interface but {@link Enum#name() name()}. Methods and fields (including the enum constants) declared by their subtypes are not excluded.
      *
      * @return created module instance
      */
@@ -78,7 +78,7 @@ public class EnumModule implements Module {
     @Override
     public void applyToConfigBuilder(SchemaGeneratorConfigBuilder builder) {
         if (this.enumConstantToString == null) {
-            // ignore all direct enum methods but name() - methods declared by a specific enum sub type are not ignored
+            // ignore all direct enum methods but name() - methods declared by a specific enum subtype are not ignored
             builder.forMethods()
                     .withIgnoreCheck(method -> EnumModule.isEnum(method.getDeclaringType()) && !"name".equals(method.getName()))
                     .withNullableCheck(method -> EnumModule.isEnum(method.getDeclaringType()) ? Boolean.FALSE : null)
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
index 9d6a7de..44cd8ab 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
@@ -72,7 +72,7 @@ public class SimpleTypeModule implements Module {
 
     /**
      * Factory method: creating an instance of the {@link SimpleTypeModule} containing mappings for various primitive types and their non-primitive
-     * counter parts (e.g. {@code boolean} and {@code Boolean}) as well as other classes that are normally serialised in a JSON as non-objects, e.g.
+     * counterparts (e.g. {@code boolean} and {@code Boolean}) as well as other classes that are normally serialised in a JSON as non-objects, e.g.
      * <br>{@code BigDecimal}, {@code BigInteger}, {@code UUID}, {@code LocalDate}, {@code LocalDateTime}, and a number of other date-time types.
      *
      * @return created module instance
@@ -241,7 +241,7 @@ public class SimpleTypeModule implements Module {
      * Specifically omit the "additionalProperties" keyword for non-object types.
      *
      * @param scope the scope to check for fixed non-object types
-     * @return either Object.class to cause omission of the "additonalProperties" keyword or null to leave it up to following configurations
+     * @return either {@code Object.class} to cause omission of the "additionalProperties" keyword or null to leave it up to following configurations
      */
     private Type resolveAdditionalProperties(TypeScope scope) {
         if (scope.getType().getTypeParameters().isEmpty()
diff --git a/jsonschema-module-jakarta-validation/src/main/java/com/github/victools/jsonschema/module/jakarta/validation/JakartaValidationModule.java b/jsonschema-module-jakarta-validation/src/main/java/com/github/victools/jsonschema/module/jakarta/validation/JakartaValidationModule.java
index faef88d..4e1889b 100644
--- a/jsonschema-module-jakarta-validation/src/main/java/com/github/victools/jsonschema/module/jakarta/validation/JakartaValidationModule.java
+++ b/jsonschema-module-jakarta-validation/src/main/java/com/github/victools/jsonschema/module/jakarta/validation/JakartaValidationModule.java
@@ -16,6 +16,7 @@
 
 package com.github.victools.jsonschema.module.jakarta.validation;
 
+import com.fasterxml.classmate.AnnotationInclusion;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.github.victools.jsonschema.generator.FieldScope;
@@ -51,6 +52,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Stream;
 
 /**
  * JSON Schema Generation Module: based on annotations from the {@code jakarta.validation.constraints} package.
@@ -112,6 +114,9 @@ public class JakartaValidationModule implements Module {
         if (this.options.contains(JakartaValidationOption.NOT_NULLABLE_METHOD_IS_REQUIRED)) {
             methodConfigPart.withRequiredCheck(this::isRequired);
         }
+        Stream.of(DecimalMax.class, DecimalMin.class, Email.class, Max.class, Min.class, Negative.class, NegativeOrZero.class,
+                        NotBlank.class, NotEmpty.class, Null.class, NotNull.class, Pattern.class, Positive.class, PositiveOrZero.class, Size.class)
+                .forEach(annotationType -> builder.withAnnotationInclusionOverride(annotationType, AnnotationInclusion.INCLUDE_AND_INHERIT));
     }
 
     /**
@@ -194,7 +199,8 @@ public class JakartaValidationModule implements Module {
         }
         // check that the annotation's validation groups have at least one common entry to the configured groups on this module
         Class<?>[] associatedGroups = validationGroupsLookup.apply(annotation);
-        return associatedGroups.length == 0 || !Collections.disjoint(this.validationGroups, Arrays.asList(associatedGroups));
+        return associatedGroups.length == 0 || Arrays.stream(associatedGroups)
+                .anyMatch(associatedGroup -> this.validationGroups.stream().anyMatch(associatedGroup::isAssignableFrom));
     }
 
     /**
diff --git a/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java b/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java
index 46c40b2..21edeaa 100644
--- a/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java
+++ b/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java
@@ -16,6 +16,7 @@
 
 package com.github.victools.jsonschema.module.javax.validation;
 
+import com.fasterxml.classmate.AnnotationInclusion;
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.MemberScope;
 import com.github.victools.jsonschema.generator.MethodScope;
@@ -24,13 +25,13 @@ import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
 import java.lang.annotation.Annotation;
 import java.math.BigDecimal;
-import java.nio.file.DirectoryStream;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Stream;
 import javax.validation.Constraint;
 import javax.validation.constraints.DecimalMax;
 import javax.validation.constraints.DecimalMin;
@@ -108,6 +109,9 @@ public class JavaxValidationModule implements Module {
         if (this.options.contains(JavaxValidationOption.NOT_NULLABLE_METHOD_IS_REQUIRED)) {
             methodConfigPart.withRequiredCheck(this::isRequired);
         }
+        Stream.of(DecimalMax.class, DecimalMin.class, Email.class, Max.class, Min.class, Negative.class, NegativeOrZero.class,
+                        NotBlank.class, NotEmpty.class, Null.class, NotNull.class, Pattern.class, Positive.class, PositiveOrZero.class, Size.class)
+                .forEach(annotationType -> builder.withAnnotationInclusionOverride(annotationType, AnnotationInclusion.INCLUDE_AND_INHERIT));
     }
 
     /**
@@ -189,7 +193,8 @@ public class JavaxValidationModule implements Module {
         }
         // check that the annotation's validation groups have at least one common entry to the configured groups on this module
         Class<?>[] associatedGroups = validationGroupsLookup.apply(annotation);
-        return associatedGroups.length == 0 || !Collections.disjoint(this.validationGroups, Arrays.asList(associatedGroups));
+        return associatedGroups.length == 0 || Arrays.stream(associatedGroups)
+                .anyMatch(associatedGroup -> this.validationGroups.stream().anyMatch(associatedGroup::isAssignableFrom));
     }
 
     /**


Test patch:
diff --git a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaBuilderTest.java b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaBuilderTest.java
index 7217afd..85e16be 100644
--- a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaBuilderTest.java
+++ b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaBuilderTest.java
@@ -35,7 +35,7 @@ public class SchemaBuilderTest {
         this.config = Mockito.spy(new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON)
                 .with(Option.PLAIN_DEFINITION_KEYS)
                 .build());
-        this.typeContext = TypeContextFactory.createDefaultTypeContext();
+        this.typeContext = TypeContextFactory.createDefaultTypeContext(this.config);
     }
 
     @Test
diff --git a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/AttributeCollectorTest.java b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/AttributeCollectorTest.java
index ca80ab2..ff39c3f 100644
--- a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/AttributeCollectorTest.java
+++ b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/AttributeCollectorTest.java
@@ -67,7 +67,8 @@ public class AttributeCollectorTest {
                 Stream.of(options).collect(Collectors.toSet()),
                 new SchemaGeneratorGeneralConfigPart(),
                 new SchemaGeneratorConfigPart<>(),
-                new SchemaGeneratorConfigPart<>());
+                new SchemaGeneratorConfigPart<>(),
+                Collections.emptyMap());
         return new SchemaGenerationContextImpl(generatorConfig, TypeContextFactory.createDefaultTypeContext());
     }
 
diff --git a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
index 6a6e477..c2da7fe 100644
--- a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
+++ b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
@@ -77,7 +77,7 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
         this.enabledOptions = new HashSet<>();
 
         this.instance = new SchemaGeneratorConfigImpl(this.objectMapper, SchemaVersion.DRAFT_2019_09, this.enabledOptions,
-                this.typesInGeneralConfigPart, this.fieldConfigPart, this.methodConfigPart);
+                this.typesInGeneralConfigPart, this.fieldConfigPart, this.methodConfigPart, Collections.emptyMap());
         this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
     }
 
diff --git a/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/IntegrationTest.java b/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/IntegrationTest.java
index dd9f466..3a8f438 100644
--- a/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/IntegrationTest.java
+++ b/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/IntegrationTest.java
@@ -91,6 +91,7 @@ public class IntegrationTest {
 
         @Null
         public Object nullObject;
+        public Book inheritedAnnotation;
 
         @NotNull
         public List<@Min(2) @Max(2048) Integer> notNullList;
@@ -135,6 +136,20 @@ public class IntegrationTest {
         public double exclusiveRangeDouble;
     }
 
+    static class Book implements Publication {
+        private String title;
+
+        @Override
+        public String getTitle() {
+            return this.title;
+        }
+    }
+
+    interface Publication {
+        @NotEmpty
+        String getTitle();
+    }
+
     @NotNull
     @Email(regexp = ".+@.+\\..+")
     @Constraint(validatedBy = {})
diff --git a/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/JakartaValidationModuleTest.java b/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/JakartaValidationModuleTest.java
index d09ee5b..a73997c 100644
--- a/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/JakartaValidationModuleTest.java
+++ b/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/JakartaValidationModuleTest.java
@@ -16,6 +16,7 @@
 
 package com.github.victools.jsonschema.module.jakarta.validation;
 
+import com.fasterxml.classmate.AnnotationInclusion;
 import com.github.victools.jsonschema.generator.ConfigFunction;
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.InstanceAttributeOverrideV2;
@@ -158,6 +159,9 @@ public class JakartaValidationModuleTest {
         Mockito.verify(this.methodConfigPart).withNumberInclusiveMaximumResolver(Mockito.any());
         Mockito.verify(this.methodConfigPart).withNumberExclusiveMaximumResolver(Mockito.any());
         Mockito.verify(this.methodConfigPart).withInstanceAttributeOverride(Mockito.any(InstanceAttributeOverrideV2.class));
+
+        Mockito.verify(this.configBuilder, Mockito.times(15))
+                .withAnnotationInclusionOverride(Mockito.any(), Mockito.eq(AnnotationInclusion.INCLUDE_AND_INHERIT));
     }
 
     static Stream<Arguments> parametersForTestNullableCheck() {
diff --git a/jsonschema-module-jakarta-validation/src/test/resources/com/github/victools/jsonschema/module/jakarta/validation/integration-test-result.json b/jsonschema-module-jakarta-validation/src/test/resources/com/github/victools/jsonschema/module/jakarta/validation/integration-test-result.json
index 6a58e85..fe43abf 100644
--- a/jsonschema-module-jakarta-validation/src/test/resources/com/github/victools/jsonschema/module/jakarta/validation/integration-test-result.json
+++ b/jsonschema-module-jakarta-validation/src/test/resources/com/github/victools/jsonschema/module/jakarta/validation/integration-test-result.json
@@ -12,6 +12,16 @@
             "minimum": 7,
             "maximum": 38
         },
+        "inheritedAnnotation": {
+            "type": "object",
+            "properties": {
+                "title": {
+                    "type": "string",
+                    "minLength": 1
+                }
+            },
+            "required": ["title"]
+        },
         "notBlankText": {
             "type": "string",
             "minLength": 1
diff --git a/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/IntegrationTest.java b/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/IntegrationTest.java
index 3313c58..ccefe5f 100644
--- a/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/IntegrationTest.java
+++ b/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/IntegrationTest.java
@@ -88,6 +88,7 @@ public class IntegrationTest {
 
         @Null
         public Object nullObject;
+        public Book inheritedAnnotation;
 
         @NotNull
         public List<@Min(2) @Max(2048) Integer> notNullList;
@@ -127,6 +128,20 @@ public class IntegrationTest {
         public double exclusiveRangeDouble;
     }
 
+    static class Book implements Publication {
+        private String title;
+
+        @Override
+        public String getTitle() {
+            return this.title;
+        }
+    }
+
+    interface Publication {
+        @NotEmpty
+        String getTitle();
+    }
+
     @NotNull
     @Email(regexp = ".+@.+\\..+")
     @Constraint(validatedBy = {})
diff --git a/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModuleTest.java b/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModuleTest.java
index f020626..ec25ecf 100644
--- a/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModuleTest.java
+++ b/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModuleTest.java
@@ -16,6 +16,7 @@
 
 package com.github.victools.jsonschema.module.javax.validation;
 
+import com.fasterxml.classmate.AnnotationInclusion;
 import com.github.victools.jsonschema.generator.ConfigFunction;
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.MethodScope;
@@ -155,6 +156,9 @@ public class JavaxValidationModuleTest {
         Mockito.verify(this.methodConfigPart).withNumberExclusiveMinimumResolver(Mockito.any());
         Mockito.verify(this.methodConfigPart).withNumberInclusiveMaximumResolver(Mockito.any());
         Mockito.verify(this.methodConfigPart).withNumberExclusiveMaximumResolver(Mockito.any());
+
+        Mockito.verify(this.configBuilder, Mockito.times(15))
+                .withAnnotationInclusionOverride(Mockito.any(), Mockito.eq(AnnotationInclusion.INCLUDE_AND_INHERIT));
     }
 
     static Stream<Arguments> parametersForTestNullableCheck() {
diff --git a/jsonschema-module-javax-validation/src/test/resources/com/github/victools/jsonschema/module/javax/validation/integration-test-result.json b/jsonschema-module-javax-validation/src/test/resources/com/github/victools/jsonschema/module/javax/validation/integration-test-result.json
index 17ba23d..33ad4ff 100644
--- a/jsonschema-module-javax-validation/src/test/resources/com/github/victools/jsonschema/module/javax/validation/integration-test-result.json
+++ b/jsonschema-module-javax-validation/src/test/resources/com/github/victools/jsonschema/module/javax/validation/integration-test-result.json
@@ -12,6 +12,16 @@
             "minimum": 7,
             "maximum": 38
         },
+        "inheritedAnnotation": {
+            "type": "object",
+            "properties": {
+                "title": {
+                    "type": "string",
+                    "minLength": 1
+                }
+            },
+            "required": ["title"]
+        },
         "notBlankText": {
             "type": "string",
             "minLength": 1