Class MoveSagaFieldToHandlerParameter

java.lang.Object
org.openrewrite.Recipe
org.axonframework.migration.MoveSagaFieldToHandlerParameter
All Implemented Interfaces:
Cloneable

public class MoveSagaFieldToHandlerParameter extends org.openrewrite.Recipe
Moves a Saga field into a parameter of the Saga methods that use it, because the Axoniq Framework legacy module does not inject Saga fields.

Axon Framework 4 populated @Autowired or @Inject fields of a Saga instance through a ResourceInjector, and called its @Autowired or @Inject setters. The legacy module resolves Saga collaborators as handler method parameters instead, from the configuration or, with Spring, from the application context, so an injected field stays null. Which fields move depends on the type option:

  • with a type, the private field of that type, injected directly or through a setter. Such a field that no method uses is removed;
  • without a type, every private field an injection setter assigns, whatever its type, when a method uses it. Its parameters are declared like the parameter of the setter, annotations such as @Qualifier included. Such a field that no method uses stays, together with its setter, for MarkSagaResourceInjection to mark.
For each field it moves, this recipe:
  • adds a parameter of the field's type, named after the field, to every @SagaEventHandler and @DeadlineHandler method that uses the field, directly or through private helper methods. With a type, a parameter of that type the method already declares is reused instead. A @DeadlineHandler without parameters first gets a DeadlineMessage parameter: the collaborator resolves from the configuration, which ranks below the payload, so as the first parameter it would become the payload and the handler would stop matching its deadlines;
  • adds the same parameter to those private helper methods, and passes it along at their call sites;
  • removes the field, together with its annotations and leading comments, once nothing refers to it;
  • removes the @Autowired or @Inject setter methods through which Axon Framework 4 injected the field, being injected methods that only assign their single parameter to it, as the legacy module does not call them either.
The calls on the collaborator itself are left as they are. When the field name clashes with a parameter or local variable of a method, the new parameter gets a numeric suffix and the field references in that method are renamed.

The migration is all-or-nothing per field. A use the recipe cannot move into a handler parameter leaves the field and every one of its uses untouched, rather than producing a half-migrated Saga. Such a use is one in a constructor, a static method, a non-private method that is not a handler (it may be called from outside the class), a field initializer or initializer block, a nested, local or anonymous class, a method reference to an affected method or to an injection setter, a call of an injection setter, an assignment to the field outside an injection setter, an access through another instance, or a call of a @DeadlineHandler without parameters, which could not pass on its new DeadlineMessage parameter. The same applies when the class declares more than one field of the configured type. Without a type, a field also stays when a handler parameter would not receive what Axon Framework 4 passed its setter: a setter with another annotation, such as @Qualifier or @Override, with an annotation argument, such as required = false, overriding a supertype method, or with a @Value parameter.

Uses in other classes, such as subclasses, are not visible to the recipe, so only a field no other class can use is moved: a private field, or in Kotlin, where properties are public by default, any property of a final class. A Kotlin class is final unless declared open, abstract or sealed. A protected, package-private or public field stays where it is. A field left in place is marked by MarkSagaResourceInjection when it, or a setter assigning it, is injected; one that is not injected was never populated by Axon Framework 4 either, and needs no marker.

Only Sagas are changed: classes annotated with the Spring @Saga stereotype or declaring a @SagaEventHandler method. Other components, such as aggregates or Spring services, keep their fields, because they are still injected there. Java and Kotlin sources are supported. The recipe is idempotent.

Since:
5.4.0
Author:
Mateusz Nowak
  • Nested Class Summary

    Nested classes/interfaces inherited from class org.openrewrite.Recipe

    org.openrewrite.Recipe.Builder, org.openrewrite.Recipe.DelegatingRecipe
  • Field Summary

    Fields inherited from class org.openrewrite.Recipe

    contributors, examples, PANIC
  • Constructor Summary

    Constructors
    Constructor
    Description
    MoveSagaFieldToHandlerParameter(@Nullable String fullyQualifiedTypeName)
    Creates the recipe for Saga fields of the given type, or for the fields injection setters assign.
  • Method Summary

    Modifier and Type
    Method
    Description
     
     
    @Nullable String
    Returns the fully qualified name of the field type this recipe moves into handler parameters.
    org.openrewrite.TreeVisitor<?,org.openrewrite.ExecutionContext>
     

    Methods inherited from class org.openrewrite.Recipe

    addDataTable, aggregateDataTableDescriptors, builder, buildRecipeList, causesAnotherCycle, clone, createRecipeDescriptor, equals, getContributors, getDataTableDescriptors, getDescriptor, getEstimatedEffortPerOccurrence, getExamples, getInstanceName, getInstanceNameSuffix, getJacksonPolymorphicTypeTag, getMaintainers, getName, getRecipeList, getTags, hashCode, maxCycles, noop, onComplete, run, run, run, setContributors, setExamples, validate, validate, validateAll, validateAll, withOptions

    Methods inherited from class java.lang.Object

    finalize, getClass, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • MoveSagaFieldToHandlerParameter

      public MoveSagaFieldToHandlerParameter(@Nullable String fullyQualifiedTypeName)
      Creates the recipe for Saga fields of the given type, or for the fields injection setters assign.
      Parameters:
      fullyQualifiedTypeName - the fully qualified name of the field type to move into handler parameters, or null to move every field an injection setter assigns, whatever its type
  • Method Details

    • getFullyQualifiedTypeName

      public @Nullable String getFullyQualifiedTypeName()
      Returns the fully qualified name of the field type this recipe moves into handler parameters.
      Returns:
      the fully qualified name of the field type, or null when the recipe moves every field an injection setter assigns
    • getDisplayName

      public String getDisplayName()
      Specified by:
      getDisplayName in class org.openrewrite.Recipe
    • getDescription

      public String getDescription()
      Specified by:
      getDescription in class org.openrewrite.Recipe
    • getVisitor

      public org.openrewrite.TreeVisitor<?,org.openrewrite.ExecutionContext> getVisitor()
      Overrides:
      getVisitor in class org.openrewrite.Recipe