From 740013f3a00f484ae819d013b49d93f786f3658b Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Wed, 10 Jul 2024 22:15:34 -0400 Subject: [PATCH] Adds changes from PR 58 https://github.com/avaje/website-source/pull/58 --- config/index.html | 4 +- inject/index.html | 4 +- jsonb/index.html | 80 ++++++++++++++++++--------------------- validator/index.html | 90 +++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 126 insertions(+), 52 deletions(-) diff --git a/config/index.html b/config/index.html index 97f8f9c..5cda16e 100644 --- a/config/index.html +++ b/config/index.html @@ -491,7 +491,7 @@

ConfigurationSource Plugins

Plugins implement the ConfigurationSource interface and are found and registered via ServiceLoader. This means they have a - file at src/main/resources/META-INF/services/io.avaje.config.ConfigurationSource + file at src/main/resources/META-INF/services/io.avaje.config.ConfigExtension which contains the class name of the implementation.

@@ -517,7 +517,7 @@

Event Logging

By default, avaje-config will immediately log initialization events to it's own configured system logger. If you want to use your own configured logger, you can extend the ConfigurationLog interface and register via ServiceLoader. This means you have a - file at src/main/resources/META-INF/services/io.avaje.config.ConfigurationLog + file at src/main/resources/META-INF/services/io.avaje.config.ConfigExtension which contains the class name of the implementation.

diff --git a/inject/index.html b/inject/index.html index c00be95..80f6c23 100644 --- a/inject/index.html +++ b/inject/index.html @@ -459,13 +459,13 @@

Java Module Setup

Example module-info
-
import io.avaje.inject.spi.InjectSPI;
+
import io.avaje.inject.spi.InjectExtension;
 
 module org.example {
 
   requires io.avaje.inject;
   // you must define the fully qualified class name of the generated classes. if you use an import statement, compilation will fail
-  provides InjectSPI with org.example.ExampleModule;
+  provides InjectExtension with org.example.ExampleModule;
 }
 
diff --git a/jsonb/index.html b/jsonb/index.html index 144c716..a0fc024 100644 --- a/jsonb/index.html +++ b/jsonb/index.html @@ -266,11 +266,11 @@

Java Module Setup

  1. Add a requires clause for io.avaje.jsonb
  2. -
  3. Add a provides clause for io.avaje.jsonb.Jsonb.GeneratedComponent
  4. +
  5. Add a provides clause for io.avaje.jsonb.spi.JsonbExtension
Example module-info
-
import io.avaje.jsonb.Jsonb.GeneratedComponent;
+  
import io.avaje.jsonb.spi.JsonbExtension;
 
 module org.example {
 
@@ -278,6 +278,7 @@ 
Example module-info
// you must define the fully qualified class name of the generated classes. if you use an import statement, compilation will fail provides GeneratedComponent with org.example.jsonb.GeneratedComponent; + provides JsonbExtension with org.example.jsonb.GeneratedComponent; //if running using Jlink application images with avaje-inject add: //requires io.avaje.jsonb.plugin; @@ -956,29 +957,28 @@

Enum Mapping with @Json.Value

@Json.Mixin

- Mark this Class as a MixIn Type that can add Jsonb Annotations on the specified type. -
- Say we want to override the field serialization behavior on a class we can't modify.(Typically in an external - project/dependency or otherwise) + Say we want to override the field serialization behavior on a class we can't modify (For example, a class in an external + project/dependency). We can use @Mixin to add Jsonb Annotations to the specified type.

-
public class MixinTarget {
+  
public class Kingfisher {
   private String name;
-  private String stand;
-  private String bandReference;
   //getters/setters...
 }
 
-

We can use the @Json.Mixin annotation on an abstract class to effectively add @Json Annotations

-
@Json.MixIn(MixinTarget.class)
-public abstract class MixinClass {
+  

Given the above class, we can use the @Json.Mixin annotation on an abstract class to effectively add @Json annotations and even change how the class is constructed

+
@MixIn(Kingfisher.class)
+public abstract class KingfisherMixin {
 
-  @Json.Property("part")
-  private String name;
+  @Json.Property("species")
+  String name;
 
-  @Json.Ignore
-  private String stand;
+  //we can even change how the target is constructed with @Json.Creator
+  @Json.Creator
+  static Kingfisher construct(String name) {
+    return new Kingfisher(species);
+  }
 }
 
@@ -987,57 +987,51 @@

@Json.Mixin

Generated Code: (click to expand)

@Json.Mixin makes the following changes to the generated MixinTargetJsonAdapter:

-
@Generated
-public final class MixinTargetJsonAdapter implements JsonAdapter<MixinTarget>, ViewBuilderAware {
+    
@Generated("io.avaje.jsonb.generator")
+public final class KingfisherJsonAdapter implements JsonAdapter<Kingfisher>, ViewBuilderAware {
 
   ...
-  public MixinTargetJsonAdapter(Jsonb jsonb) {
+
+  public KingfisherJsonAdapter(Jsonb jsonb) {
     this.stringJsonAdapter = jsonb.adapter(String.class);
-    //the mixin class renamed "name" property to "part"
-    this.names = jsonb.properties("part", "stand", "bandReference");
+    //name attribute replaced with species
+    this.names = jsonb.properties("species");
   }
 
   ...
 
   @Override
-  public void toJson(JsonWriter writer, MixinTarget mixinTarget) {
+  public void toJson(JsonWriter writer, Kingfisher kingfisher) {
     writer.beginObject(names);
     writer.name(0);
-    stringJsonAdapter.toJson(writer, mixinTarget.getName());
-    writer.name(2);
-    // stand property is absent
-    stringJsonAdapter.toJson(writer, mixinTarget.getBandReference());
+    stringJsonAdapter.toJson(writer, kingfisher.getName());
     writer.endObject();
   }
 
   @Override
-  public MixinTarget fromJson(JsonReader reader) {
-    MixinTarget _$mixinTarget = new MixinTarget();
+  public Kingfisher fromJson(JsonReader reader) {
+    // variables to read json values into, constructor params don't need _set$ flags
+    String     _val$name = null;
 
     // read json
     reader.beginObject(names);
     while (reader.hasNextField()) {
       final String fieldName = reader.nextField();
       switch (fieldName) {
-        case "part": {
-          _$mixinTarget.setName(stringJsonAdapter.fromJson(reader)); break;
-        }
-        case "stand": {
-          //now ignored
-          reader.skipValue(); break;
-        }
-        case "bandReference": {
-          _$mixinTarget.setBandReference(stringJsonAdapter.fromJson(reader)); break;
-        }
-        default: {
+        case "species":
+          _val$name = stringJsonAdapter.fromJson(reader);
+          break;
+
+        default:
           reader.unmappedField(fieldName);
           reader.skipValue();
-        }
       }
     }
     reader.endObject();
 
-    return _$mixinTarget;
+    // build and return Kingfisher using the mixin creator
+    Kingfisher _$kingfisher = KingfisherMixin.construct(_val$name);
+    return _$kingfisher;
   }
 }
 
@@ -1049,8 +1043,8 @@

@Json.Subtype

For mapping polymorphic types we specify on the parent type a @Json.Subtype for each concrete sub-type that can represent that type. -

By default the "type property" that specifies the type in json is "@type". Use - @Json(typeProperty=...) to specify the name of the type property. +

By default the "type property" that specifies the type in json is "@type". To specify alternate values for the type property, use + @Json(typeProperty=...).

Note: There is a current limitation that polymorphic types do not yet support "Json Views".

diff --git a/validator/index.html b/validator/index.html index 1769b3e..d61c812 100644 --- a/validator/index.html +++ b/validator/index.html @@ -37,6 +37,8 @@
  • Inherited Constraints
  • +
  • @Mixin +
  • Method Validation
  • Constraint Error Message @@ -218,17 +220,14 @@

    Java Module Setup

    Example module-info
    import io.avaje.validation.Validator.GeneratedComponent;
    -import io.avaje.validation.spi.ValidatorCustomizer;
    +import io.avaje.validation.spi.ValidationExtension;
     
     module org.example {
     
       requires io.avaje.validator;
     
    -  //if defining validation customizers add:
    -  provides ValidatorCustomizer with org.example.validator.MyCustomizer;
    -
       // you must define the fully qualified class name of the generated classes. if you use an import statement, compilation will fail
    -  provides GeneratedComponent with org.example.validator.GeneratedComponent;
    +  provides ValidationExtension with org.example.validator.GeneratedComponent;
     
       //if running using Jlink application images with avaje-inject add:
       //requires io.avaje.validator.plugin;
    @@ -470,6 +469,87 @@ 

    Inherited Constraints

    evaluated in addition to the @NotNull constraint from the superclass.

    +

    @Mixin

    + +

    + Say we want to override the validation behavior on a class we can't modify. (For example, a class in an external + project/dependency). We can use @Mixin to add/override constraint annotations on the specified type. +

    + +
    public class Swallow {
    +  private String species;
    +  @Positive
    +  private int avgSpeed;
    +  @AssertTrue
    +  private boolean isLaden;
    +  //getters/setters...
    +}
    +
    +
    +

    Given the above class, we can use the @Mixin annotation on an abstract class to effectively add/override constraint annotations.

    +
    @MixIn(Swallow.class)
    +@EntryRiddleCheck //class constraints can be added as well
    +public abstract class SwallowMixin {
    +  //Add NotBlank
    +  @NotBlank("species shouldn't be null")
    +  String species;
    +
    +  //adding the target field without any annotations disables the constraints
    +  boolean isLaden;
    +}
    +
    +
    + +
    + Generated Code: (click to expand) +

    Given the above mixin, the SwallowValidationAdapter becomes:

    + +
    @Generated("avaje-validator-generator")
    +public final class SwallowValidationAdapter implements ValidationAdapter<Swallow> {
    +
    +  private final ValidationAdapter<String> speciesValidationAdapter;
    +  private final ValidationAdapter.Primitive avgSpeedValidationAdapter;
    +  private final ValidationAdapter<Swallow> swallowValidationAdapter;
    +
    +  public SwallowValidationAdapter(ValidationContext ctx) {
    +    this.speciesValidationAdapter =
    +        ctx.<String>adapter(NotBlank.class, Map.of("max",0, "message","species shouldn\'t be null", "_type","String"));
    +
    +    this.avgSpeedValidationAdapter =
    +        ctx.<Integer>adapter(Positive.class, Map.of("message","{avaje.Positive.message}", "_type","Integer"))
    +            .primitive();
    +
    +    this.swallowValidationAdapter =
    +        ctx.<Swallow>adapter(EntryRiddleCheck.class, Map.of());
    +
    +  }
    +
    +  @Override
    +  public boolean validate(Swallow value, ValidationRequest request, String field) {
    +    if (field != null) {
    +      request.pushPath(field);
    +    }
    +    var _$species = value.getSpecies();
    +    speciesValidationAdapter.validate(_$species, request, "species");
    +
    +    var _$avgSpeed = value.getAvgSpeed();
    +    avgSpeedValidationAdapter.validate(_$avgSpeed, request, "avgSpeed");
    +
    +    if (!request.hasViolations()) {
    +      swallowValidationAdapter.validate(value, request, field);
    +    }
    +
    +
    +    if (field != null) {
    +      request.popPath();
    +    }
    +    return true;
    +  }
    +}
    +
    +
    +
    +

    Method Validation

    Constraints can not only be applied to POJOs and their properties, but also to the