Interface MatsInitiateInterceptor

All Known Subinterfaces:
MatsInitiateInterceptor.MatsInitiateInterceptOutgoingMessages
All Known Implementing Classes:
LocalStatsMatsInterceptor, MatsMetricsLoggingInterceptor, MatsMicrometerInterceptor

public interface MatsInitiateInterceptor
Implement this interface to intercept Initiations, then register with MatsInterceptable.addInitiationInterceptor(MatsInitiateInterceptor).

Meant for intercepting initiations with ability to modify the initiation, and to implement extra logging and metrics gathering.

When more than one interceptor is installed, the ordering becomes interesting. If the following class is added twice, first with 1 as the argument, and then secondly with 2 as the argument ..:

 private static class MyMatsInitiateInterceptor implements MatsInitiateInterceptor {

     private final int _number;

     MyMatsInitiateInterceptor(int number) {
         _number = number;
     }

     @Override
     public void initiateStarted(InitiateStartedContext initiateStartedContext) {
         log.info("Started #" + _number);
     }

     @Override
     public void initiateIntercept(InitiateInterceptContext initiateInterceptContext,
             InitiateLambda initiateLambda, MatsInitiate matsInitiate) {
         log.info("Intercept pre #" + _number);

         // Wrap the MatsInitiate to catch "send(dto)", before invoking the lambda
         MatsInitiateWrapper wrappedMatsInitiate = new MatsInitiateWrapper(matsInitiate) {
             @Override
             public MessageReference send(Object messageDto) {
                 log.info(".. send pre #" + _number);
                 MessageReference send = super.send(messageDto);
                 log.info(".. send post #" + _number);
                 return send;
             }
         };

         // Invoke the lambda, with the wrapped MatsInitiate
         initiateLambda.initiate(wrappedMatsInitiate);

         log.info("Intercept post #" + _number);
     }

     @Override
     public void initiateCompleted(InitiateCompletedContext initiateCompletedContext) {
         log.info("Completed #" + _number);
     }
 }
 
.. then the following sequence of log lines and operations ensues:
  1. First the initiateStarted(..) are invoked in sequence:
  2. Started #1
  3. Started #2
  4. Then the initiation goes into the transaction, and a "reverse stack" of lambdas are generated from the two interceptors' initiateIntercept(..) methods, and then the resulting lambda is invoked:
  5. Intercept pre #1
  6. Intercept pre #2
  7. .. the actual user provided lambda (the one provided by the endpoint) is invoked, performing a send(..), which goes through the stack of MatsInitiateWrappers which the initiateIntercept(..) invocations generated (thus hitting the last wrapping by #2 first):
  8. .. send pre #2
  9. .. send pre #1
  10. .. the actual Mats API send(..) method is invoked, before we traverse out of the MatsInitiateWrappers:
  11. .. send post #1
  12. .. send post #2
  13. .. and then the user lambda exits, traversing back up the initiateIntercept(..) stack
  14. Intercept post #2
  15. Intercept post #1
  16. Finally, the {link initiateComplete(..) is invoked, in explicit reversed order:
  17. Completed #2
  18. Completed #1
If you envision that these two interceptors are created for logging, where the first interceptor puts values on the MDC, while the second one logs, then this makes a bit of sense: First the MDC is set, then an entry log line is emitted, then an exit log line is emitted, then the MDC is cleared. However, interceptions of the inner call from the actual user provided lambda to MatsInitiate.send(..) ends up in what is seemingly the "wrong" sequential order, due to how the interception is logically performed: The #2 is the last initiateIntercept(..) that is invoked, which thus is the code that lastly wraps the MatsInitiate instance, and hence when the user code invokes matsInitiate.send(..), that lastly added wrapping is the first to be executed. What this means, is that you should probably not make severe ordering dependencies between interceptors which depends on wrapping the MatsInitiate instance, as your head will most probably hurt from the ensuing twisted reasoning!

Note: This is only invoked for proper, actual initiations "from outside of Mats", i.e. using a MatsInitiator gotten with MatsFactory.getDefaultInitiator() or MatsFactory.getOrCreateInitiator(String) - and notice the special semantics of getDefaultInitiator(), whereby if such an seemingly "from the outside" initiation is invoked when code-flow-wise within a Stage, you will actually be "elevated" to be initiated "within Mats" using the Stage initiator - and hence this interceptor will not be invoked (it is no longer "from outside of Mats").

The concept of "outside" vs. "inside" perhaps seems subtle, but there is a distinct difference: No processing will ever happen in a Mats fabric if no initiations happens "from the outside": It is always a "from the outside" initiation that will set Mats flows in action. Such a process flow might then set several new Mats flows in action (i.e. initiations "from the inside"), but those are dependent on the initial Mats flow that was set in motion "from the outside", and would never have been initiated was it not for such initiation.

To catch initiations "from the inside", you will employ a MatsStageInterceptor.