Package io.mats3

Class MatsInitiator.MatsInitiateWrapper

java.lang.Object
io.mats3.MatsInitiator.MatsInitiateWrapper
All Implemented Interfaces:
MatsFactory.MatsWrapper<MatsInitiator.MatsInitiate>, MatsInitiator.MatsInitiate
Enclosing interface:
MatsInitiator

public static class MatsInitiator.MatsInitiateWrapper extends Object implements MatsFactory.MatsWrapper<MatsInitiator.MatsInitiate>, MatsInitiator.MatsInitiate
A base Wrapper for MatsInitiator.MatsInitiate, which simply implements MatsInitiate, takes a MatsInitiate instance and forwards all calls to that. Use this if you need to wrap the MatsInitiate, where most of the methods are pass-through to the target, as any changes to the MatsInitiate interface then won't break your wrapper.
  • Constructor Details

    • MatsInitiateWrapper

      public MatsInitiateWrapper(MatsInitiator.MatsInitiate targetMatsInitiate)
      Standard constructor, taking the wrapped MatsInitiator.MatsInitiate instance.
      Parameters:
      targetMatsInitiate - the MatsFactory instance which unwrap() will return (and hence all forwarded methods will use).
    • MatsInitiateWrapper

      public MatsInitiateWrapper()
      No-args constructor, which implies that you either need to invoke setWrappee(MatsInitiate) before publishing the instance (making it available for other threads), or override unwrap() to provide the desired MatsInitiator.MatsInitiate instance. In these cases, make sure to honor memory visibility semantics - i.e. establish a happens-before edge between the setting of the instance and any other threads getting it.
  • Method Details

    • setWrappee

      public void setWrappee(MatsInitiator.MatsInitiate targetMatsInitiate)
      Sets the wrapped MatsInitiator.MatsInitiate, e.g. in case you instantiated it with the no-args constructor. Do note that the field holding the wrapped instance is not volatile nor synchronized. This means that if you want to set it after it has been published to other threads, you will have to override both this method and unwrap() to provide for needed memory visibility semantics, i.e. establish a happens-before edge between the setting of the instance and any other threads getting it. A volatile field would work nice.
      Specified by:
      setWrappee in interface MatsFactory.MatsWrapper<MatsInitiator.MatsInitiate>
      Parameters:
      targetMatsInitiate - the MatsInitiator.MatsInitiate which is returned by unwrap(), unless that is overridden.
    • unwrap

      public MatsInitiator.MatsInitiate unwrap()
      Specified by:
      unwrap in interface MatsFactory.MatsWrapper<MatsInitiator.MatsInitiate>
      Returns:
      the wrapped MatsInitiator.MatsInitiate. All forwarding methods invokes this method to get the wrapped MatsInitiator.MatsInitiate, thus if you want to get creative wrt. how and when the MatsInitiate is decided, you can override this method.
    • unwrapFully

      public MatsInitiator.MatsInitiate unwrapFully()
      Specified by:
      unwrapFully in interface MatsFactory.MatsWrapper<MatsInitiator.MatsInitiate>
      Specified by:
      unwrapFully in interface MatsInitiator.MatsInitiate
      Returns:
      the fully unwrapped instance: If the returned instance from MatsFactory.MatsWrapper.unwrap() is itself a MatsWrapper, it will recurse down by invoking this method (unwrapFully()) again on the returned target.
    • traceId

      public MatsInitiator.MatsInitiate traceId(CharSequence traceId)
      Description copied from interface: MatsInitiator.MatsInitiate
      Sets (or appends with a joining "|" in case of initiation within a stage) the supplied Trace Id, which is solely used for logging and debugging purposes. It should be unique, at least to a degree where it is very unlikely that you will have two identical traceIds within a couple of years ("guaranteed globally unique through all time" is not relevant).

      Since this is very important when doing distributed and asynchronous architectures, it is mandatory.

      The traceId follows a Mats processing from the initiation until it is finished, usually in a Terminator.

      It should be a world-unique Id, preferably set all the way back when some actual person performed some event. E.g. in a "new order" situation, the Id would best be set when the user clicked the "place order" button on the web page - or maybe even derived from the event when he first initiated the shopping cart - or maybe even when he started the session. The point is that when using e.g. Kibana or Splunk to track events that led to some outcome, a robust, versatile and information-rich track/trace Id makes wonders.

      It is strongly recommended to use small, dense, information rich Trace Ids. Sticking in an UUID as Trace Id certainly fulfils the uniqueness-requirement, but it is a crappy solution, as it by itself does not give any hint of source, cause, relevant entities, or goal. (It isn't even dense for the uniqueness an UUID gives, which also is way above the required uniqueness unless you handle billions of such messages per minute. A random alphanum (A-Z,a-z,0-9) string of much smaller length would give plenty enough uniqueness). The following would be a much better Trace Id than a random UUID, which follows some scheme that could be system wide: "Web.placeOrder[cid:43512][cart:xa4ru5285fej]qz7apy9". From this example TraceId we could infer that it originated at the Web system, it regards Placing an order for Customer Id 43512, it regards the Shopping Cart Id xa4ru5285fej, and it contains some uniqueness ('qz7apy9') generated at the initiating, so that even if the customer managed to click three times on the "place order" button for the same cart, you would still be able to separate the resulting three different Mats call flows.

      You should consider storing the traceId as a column in any inserted rows in any databases that was affected by this call flow, e.g. in the row for a placed order, you'd have the traceId as a column. This both to be able to tie this db row back to the logging system, but also if new Mats flows are initiated based on this row, you can include the order TraceId in the new TraceId (with either "|" or "+" as separator).

      For situations where one Mats Flow "forks" new Mats flows, make new TraceIds for the "sub flows" by tying together the existing TraceId and a new Sub TraceId with a "|". In the order/shipping example, the user would first have placed an order, resulting in an entry in an order database table - where the traceId was stored along. Then, when some batch process Mats Flow initiated the filling process, you would pick up the order's traceId and append that to the sub Mats Flow. Sub Flow Mats TraceId = "{Filling Batch Id}|{Existing Order TraceId}". If you took this all the way, you could in your logging system follow the "total flow" from the initial click on the web page, through the order book, order processing, shipment, and all the way to the delivered and signed package on the person's door - even if the entirety of the order fulfillment consists of multiple "stop and go" sub-flows (the stops being where the process stays for a while as an entry in a database, here "order", then possibly "filling", "shipping" and finally "delivery", or whatever your multiple processes' flows consists of).

      The "+" character should be used to indicate that an "initiated from the outside" TraceId is concatenated by multiple parts, which so far has been used to pick up an X-Request-ID header from a HTTP-request, and prepending this to the TraceId for a resulting Mats-flow, i.e. Mats TraceId="{X-Request-ID}+{Flow TraceID}". (Check out MatsFactory.FactoryConfig.setInitiateTraceIdModifier(Function) for a way to automate this.)

      The difference between "|" and "+" is meant to be that "|" indicates that there is a Mats Flow TraceId on both sides of the "|", while the "+" is used when a "from the outside"-initiated TraceId is built up by multiple pieces, e.g. X-Request-Id, or Batch Run Number, on the left side. Remember, this is all meant for human consumption, in particular when debugging. Do what you feel is meaningful - the Mats system doesn't care!

      (For the default implementation "JMS Mats", the Trace Id is set on the MDC of the SLF4J logging system, using the key "traceId". Since this implementation logs a few lines per handled message, in addition to any log lines you emit yourself, you will, by collecting the log lines in a common log system (e.g. the ELK stack), be able to very easily follow the processing trace through all the services the call flow passes.)

      Specified by:
      traceId in interface MatsInitiator.MatsInitiate
      Parameters:
      traceId - the traceId that will follow the mats trace from initiation (first call) to termination (last call, where the flow ends). Although the type is CharSequence, the implementation will immediately do toString() on the instance to fix the value.
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • keepTrace

      Description copied from interface: MatsInitiator.MatsInitiate
      Hint to the underlying implementation to which level of call and state history the underlying protocol should retain. The default (unless configured otherwise), MatsInitiator.KeepTrace.COMPACT, keeps a trail of all the stages the flow has been through, but drops state and DTOs that aren't relevant for the current message. You might want to use MatsInitiator.KeepTrace.FULL while developing new functionality, while once a certain call flow stabilizes, without much errors or DLQs, you should consider initializing it with MatsInitiator.KeepTrace.COMPACT or even MatsInitiator.KeepTrace.MINIMAL (the latter also dropping the "trail", thus only keeping information strictly relevant for the current message).

      This functionality is solely for debugging, the Mats endpoints works identically with any setting, thus it is a tradeoff between performance (size of messages) and debuggability. The resulting kept trace would typically be visible in a "toString()" of the MatsEndpoint.ProcessContext - or in an external (e.g. Brokerside) debugging/tracing system.

      Specified by:
      keepTrace in interface MatsInitiator.MatsInitiate
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • nonPersistent

      public MatsInitiator.MatsInitiate nonPersistent()
      Description copied from interface: MatsInitiator.MatsInitiate
      Enable unreliable, but fast, messaging! Hint to the underlying implementation that it does not matter that much if this message is lost. The implication is that the messages that this flow consist of are unreliable - typically, if the MQ broker is restarted, any outstanding "non persistent" messages are lost. (Also, some backends will (in default config) lose the Dead Letter Queue (DLQ) functionality when this is used, where a ordinary persistent message would be DLQed if it failed to be delivered to an endpoint. This can severely impact monitoring (as you don't get a build-up of DLQs when things goes wrong - only log lines) and to a degree debugging (since you don't have the DLQ'ed messages to look at).)

      This is only usable for "pure GET"-style requests without any state changes along the flow, i.e. "AccountService.getBalances", for display to an end user. If such a message is lost, the world won't go under.

      The upshot here is that non-persistent messaging typically is blazingly fast and is way less resource demanding, as the messages will not have to (transactionally) be stored in non-volatile storage. It is therefore wise to actually employ this feature where it makes sense (which, again, is only relevant for side-effect free GET-style requests).

      Specified by:
      nonPersistent in interface MatsInitiator.MatsInitiate
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • nonPersistent

      public MatsInitiator.MatsInitiate nonPersistent(long timeToLiveMillis)
      Description copied from interface: MatsInitiator.MatsInitiate
      Same as MatsInitiator.MatsInitiate.nonPersistent(), but you can set a time-to-live too. If the message gets this old and have not yet been delivered to the receiving Mats endpoint, it will be deleted and never delivered.

      This functionality often makes sense for messages that are both interactive and non-persistent: Such messages shall only be "getters" free of any side effects (i.e. no state is changed by the entire message flow), and where a human is actively waiting for the reply. If there is a situation where such messages aren't consumed due to the receiving service having problems, it does not make sense to use processing resources to handle a massive stack of these messages when the consumption is restored an hour later, as e.g. the synchronously waiting HTTP call that was waiting for the reply has timed out, and the waiting human is probably long gone anyway.

      Notice on use: This should NOT be employed for message flows where any stage might change any state, i.e. message flows with side effects (Think "PUT", "POST" and "DELETE"-style messages) - which also should NOT employ non-persistent messaging. The rationale is that such messages should never just cease to exists, for any reason - they should have both guaranteed delivery and execution. You should also never use this to handle any business logic, e.g. some kind of order time-out where an order is only valid until 21:00, or something like this. This both because of the "Note on implementation" below, and that the entire facility of "time to live" is optional both for Mats and for the underlying message queue system.

      Notice on implementation: If the message is a part of a multi-message flow, which most Mats initiations pretty much invariably is (a request consists of a request-message and a reply-message), this TTL will be set afresh on every new message in the flow, possibly with the amount of time taken in the processing of the stage deducted. However, the time that the message waited in queue will not be deducted. The effective TTL of the flow might therefore be a multiple of what is set here. An example: The TTL of an initiation is set to 5000 ms. The request message stays 4 seconds in queue, before being received and processed, where the processing took 100 ms. The reply-message will thus have its TTL set to 4900 ms: 5000 ms TTL - 100 ms for processing. The reply message stays 4 seconds in queue before being received. The total "time in flight" has now been 8.1 seconds, and there was still 900 ms left of the reply-message's TTL. The rationale for not deducting queue-time on the subsequent message is that there is no easy way to get the "queue time" which does not involve taking the difference between two timestamps, but in a multi-server architecture there is a clear possibility of clock skews between different services, even for instances of the same service. You could then deduce a too high queue time, deducting a too high value from the reply-message's TTL, and effectively time out the full message flow too early. However, for the intended use case - to hinder build-up of messages that will nevertheless be valueless when the answer is received since the interactively waiting human is long gone - this is no big problem.

      Specified by:
      nonPersistent in interface MatsInitiator.MatsInitiate
      Parameters:
      timeToLiveMillis - the number of milliseconds before this message is timed out and thus will never be delivered - 0 means "live forever", and this is the default.
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • interactive

      public MatsInitiator.MatsInitiate interactive()
      Description copied from interface: MatsInitiator.MatsInitiate
      Prioritize this Mats flow! Hint to the underlying implementation that a human is actually waiting for the result of a request, and that the flow therefore should be prioritized. This status will be kept through the entire flow, so that all messages in the flow are prioritized. This makes it possible to use the same "AccountService.getBalances" service both for the Web Application that the user facing GUI are employing, and the batch processing of a ton of orders. Without such a feature, the interactive usage could be backlogged by the batch process, while if the interactive flag is set, it will bypass the backlog of "ordinary" messages.

      This implies that MATS defines two levels of prioritization: "Ordinary" and "Interactive". Most processing should employ the default, i.e. "Ordinary", while places where a human is actually waiting for the reply should employ the fast-lane, i.e. "Interactive". It is imperative to not abuse this feature, or else it will loose its value: If batches are going too slow, nothing will be gained by setting the interactive flag except destroying the entire point of this feature. Instead use higher parallelism: By increasing concurrency or the number of nodes that is running the problematic endpoint or stage; increase the speed and/or throughput of external systems like the database; or somehow just code the whole thing to be faster!

      It will often make sense to set both this flag, and the MatsInitiator.MatsInitiate.nonPersistent(), at the same time. E.g. when you need to show the account balance for a customer: It both needs to skip past any bulk/batch queue of such requests (since a human is literally waiting for the result), but it is also a "pure GET"-style request, not altering state whatsoever, so it can also be set to non-persistent.

      Specified by:
      interactive in interface MatsInitiator.MatsInitiate
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • noAudit

      public MatsInitiator.MatsInitiate noAudit()
      Description copied from interface: MatsInitiator.MatsInitiate
      Marks this Mats flow as not relevant for auditing.

      When considering auditing ("event sourcing"-style) of all messages, one quickly realizes that there are very many messages that aren't that interesting to log. These are pure getters employed to show information to users, and even worse in this respect, "Are you up?"-type health checks.

      This flag is here to mark messages as such: "You will gain no historic insight in logging the following message flow!". This flag should NOT be set for ANY messages that (can potentially) change state in any part of the total system (i.e. "permanent state", e.g. a row in a database). More subtle, the flag should also not be set for "getters" that are performed to decide upon a state - e.g. for validation of new orders: The getter that checks credit should be audited. If such a getter is a part of a Mats flow, this should not be a problem, as the initiator of the "add order" Mats flow would obviously not set noAudit(). However, if it is coded as multiple "stop and go" flows, i.e. add the order to some incoming order table. Then a next, separate Mats flow is validation: That getter should be audited, hence do not set noAudit().

      Note: It might be interesting to log that such messages actually happened, but not the content of them. This is to be able to tally them, i.e. "WebService.healthCheck is invoking AccountService.webStatus 52389052 times per day" - both to see that it is probably a bit excessive, and to see that there is traffic there at all (since AccountService.webStatus seems to be a pretty specific endpoint). However, there is probably not much use in storing the contents of such calls - and maybe not retain the calls for more than a few months.

      Specified by:
      noAudit in interface MatsInitiator.MatsInitiate
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • from

      public MatsInitiator.MatsInitiate from(String initiatorId)
      Description copied from interface: MatsInitiator.MatsInitiate
      Sets the originating/initiating "synthetic endpoint Id" - only used for statistics/tracing/debugging. If this message is initiated from within a stage, i.e. by use of MatsEndpoint.ProcessContext.initiate(InitiateLambda), the 'from' property is already set to the stageId of the currently processing Stage, but it can be overridden if desired. It is important that you do not make this into a dynamic string, i.e. do not add some Id to it (such Ids should go into the traceId).

      It it is smart to decide on a common prefix for all Mats Endpoints and InitiatorIds for a particular service. E.g. "OrderService" or "InventoryService" or something like this.

      A good value that would be of use when debugging a call trace is something following a structure like "OrderService.REST.place_order_from_user", this example trying to convey that it is from the OrderSystem, coming in over its REST endpoint "place_order_from_user". Note that there are no e.g. userId there.

      NOTE: This is only used for tracing/debugging (in particular, it is not related to the replyTo functionality), but should be set to something that will give insights when you try to make sense of call flows. Think of a introspection system showing a histogram of where messages are initiated, so that you can see that 45% of the messages are coming from the OrderSystem's REST endpoints, and 15% of all initiations are its "place_order_from_user". This also implies that it shall not be a dynamic value, i.e. do not put something that will vary between each call, that is, do NOT add the user's Id or something like that. Such dynamic elements is what the MatsInitiator.MatsInitiate.traceId(CharSequence) is for.

      Specified by:
      from in interface MatsInitiator.MatsInitiate
      Parameters:
      initiatorId - the originating/initiating "synthetic endpoint Id" - only used for tracing/debugging.
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • to

      public MatsInitiator.MatsInitiate to(String endpointId)
      Description copied from interface: MatsInitiator.MatsInitiate
      Sets which MATS Endpoint this message should go.
      Specified by:
      to in interface MatsInitiator.MatsInitiate
      Parameters:
      endpointId - to which MATS Endpoint this message should go.
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • replyTo

      public MatsInitiator.MatsInitiate replyTo(String endpointId, Object replySto)
      Description copied from interface: MatsInitiator.MatsInitiate
      Specified which MATS Endpoint the reply of the invoked Endpoint should go to.
      Specified by:
      replyTo in interface MatsInitiator.MatsInitiate
      Parameters:
      endpointId - which MATS Endpoint the reply of the invoked Endpoint should go to.
      replySto - the object that should be provided as STO to the service which get the reply.
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • replyToSubscription

      public MatsInitiator.MatsInitiate replyToSubscription(String endpointId, Object replySto)
      Description copied from interface: MatsInitiator.MatsInitiate
      A "pub-sub" variant of replyTo, where the reply will go to the specified endpointId which must be a SubscriptionTerminator.
      Specified by:
      replyToSubscription in interface MatsInitiator.MatsInitiate
      Parameters:
      endpointId - which MATS Endpoint the reply of the invoked Endpoint should go to.
      replySto - the object that should be provided as STO to the service which get the reply.
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • setTraceProperty

      public MatsInitiator.MatsInitiate setTraceProperty(String propertyName, Object propertyValue)
      Description copied from interface: MatsInitiator.MatsInitiate
      Adds a property that will "stick" with the call flow from this call on out. Read more on MatsEndpoint.ProcessContext.setTraceProperty(String, Object).
      Specified by:
      setTraceProperty in interface MatsInitiator.MatsInitiate
      Parameters:
      propertyName - the name of the property
      propertyValue - the value of the property, which will be serialized using the active MATS serializer.
      See Also:
    • logMeasurement

      public MatsInitiator.MatsInitiate logMeasurement(String metricId, String metricDescription, String baseUnit, double measure, String... labelKeyValue)
      Description copied from interface: MatsInitiator.MatsInitiate
      Adds a measurement of a described variable, in a base unit, for this Initiation - be sure to understand that the three String parameters are constants for each measurement. To exemplify, you may measure five different things in an Initiation, i.e. "number of items in order", "total amount for order in dollar", etc - and each of these obviously have different metricId, metricDescription and possibly different baseUnit from each other. BUT, the specific arguments for "number of items in order" (outside the measure itself!) shall not change between one Initiation and the next (of the same "type", i.e. place in the code): e.g. the metricDescription shall NOT be dynamically constructed to e.g. say "Number of items in order 1234 for customer 5678".

      Note: It is illegal to use the same 'metricId' for more than one measurement for a given Initiation, and this also goes between measurements and timing measurements.

      Inclusion as metric by plugin 'mats-intercept-micrometer': A new meter will be created (and cached), of type DistributionSummary, with the 'name' set to "mats.exec.ops.measure.{metricId}.{baseUnit}" ("measure"->"time" for timings), and 'description' to description. (Tags/labels already added on the meter by the plugin include 'appName', 'initiatorId', 'initiatingAppName', 'stageId' (for stages), and 'initiatorName' (for inits)). Read about parameter 'labelKeyValue' below.

      Inclusion as log line by plugin 'mats-intercept-logging': A log line will be output by each added measurement, where the MDC for that log line will have an entry with key "mats.ops.measure.{metricId}.{baseUnit}". Read about parameter 'labelKeyValue' below.

      It generally makes most sense if the same metrics are added for each processing of a particular Initiation, i.e. if the "number of items" are 0, then that should also be recorded along with the "total amount for order in dollar" as 0, not just elided. Otherwise, your metrics will be skewed.

      You should use a dot-notation for the metricId if you want to add multiple meters with a hierarchical/subdivision layout.

      The vararg 'labelKeyValue' is an optional element where the String-array consist of one or several alternate key, value pairs. Do not employ this feature unless you know what the effects are, and you actually need it! This will be added as labels/tags to the metric, and added to the SLF4J MDC for the measurement log line with the key being "mats.ops.measure.{metricId}.{labelKey}" ("measure"->"time" for timings). The keys should be constants as explained for the other parameters, while the value can change, but only between a given set of values (think enum) - using e.g. the 'customerId' as value doesn't make sense and will blow up your metric cardinality. Notice that if you do employ e.g. two labels, each having one of three values, you'll effectively create 9 different meters, where your measurement will go to one of them.

      NOTICE: If you want to do a timing, then instead use MatsInitiator.MatsInitiate.logTimingMeasurement(String, String, long, String...)

      Specified by:
      logMeasurement in interface MatsInitiator.MatsInitiate
      Parameters:
      metricId - constant, short, possibly dot-separated if hierarchical, id for this particular metric, e.g. "items" or "amount", or "db.query.orders".
      metricDescription - constant, textual description for this metric, e.g. "Number of items in customer order", "Total amount of customer order"
      baseUnit - the unit for this measurement, e.g. "quantity" (for a count measure), "dollar" (for an amount), or "bytes" (for a document size).
      measure - value of the measurement
      labelKeyValue - a String-vararg array consisting of alternate key,value pairs which will becomes labels or tags or entries for the metrics and log lines. Read the JavaDoc above; the keys shall be "static" for a specific measure, while the values can change between a specific small set values.
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • logTimingMeasurement

      public MatsInitiator.MatsInitiate logTimingMeasurement(String metricId, String metricDescription, long nanos, String... labelKeyValue)
      Description copied from interface: MatsInitiator.MatsInitiate
      Same as addMeasurement(..), but specifically for timings - Read that JavaDoc!

      Note: It is illegal to use the same 'metricId' for more than one measurement for a given Initiation, and this also goes between timing measurements and measurements.

      For the metrics-plugin 'mats-intercept-micrometer' plugin, the 'baseUnit' argument is deduced to whatever is appropriate for the receiving metrics system, e.g. for Prometheus it is "seconds", even though you always record the measurement in nanoseconds using this method.

      For the logging-plugin 'mats-intercept-logging' plugin, the timing in the log line will be in milliseconds (with fractions), even though you always record the measurement in nanoseconds using this method.

      Specified by:
      logTimingMeasurement in interface MatsInitiator.MatsInitiate
      Parameters:
      metricId - constant, short, possibly dot-separated if hierarchical, id for this particular metric, e.g. "db.query.orders" or "calcprofit".
      metricDescription - constant, textual description for this metric, e.g. "Time taken to execute order query", "Time taken to calculate profit or loss".
      nanos - time taken in nanoseconds
      labelKeyValue - a String-vararg array consisting of alternate key,value pairs which will becomes labels or tags or entries for the metrics and log lines. Read the JavaDoc at addMeasurement(..)
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
    • addBytes

      public MatsInitiator.MatsInitiate addBytes(String key, byte[] payload)
      Description copied from interface: MatsInitiator.MatsInitiate
      Adds a binary payload to the outgoing request message, e.g. a PDF document.

      The rationale for having this is to not have to encode a largish byte array inside the JSON structure that carries the Request DTO - byte arrays represent very badly in JSON.

      Note: The byte array is not compressed (as might happen with the DTO), so if the payload is large, you might want to consider compressing it before attaching it (and will then have to decompress it on the receiving side).

      Specified by:
      addBytes in interface MatsInitiator.MatsInitiate
      Parameters:
      key - the key on which to store the byte array payload. The receiver will have to use this key to get the payload out again, so either it will be a specific key that the sender and receiver agree upon, or you could generate a random key, and reference this key as a field in the Request DTO.
      payload - the byte array.
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
      See Also:
    • addString

      public MatsInitiator.MatsInitiate addString(String key, String payload)
      Description copied from interface: MatsInitiator.MatsInitiate
      Adds a String payload to the outgoing request message, e.g. a XML, JSON or CSV document.

      The rationale for having this is to not have to encode a largish string document inside the JSON structure that carries the Request DTO.

      Note: The String payload is not compressed (as might happen with the DTO), so if the payload is large, you might want to consider compressing it before attaching it and instead use the addBytes(..) method (and will then have to decompress it on the receiving side).

      Specified by:
      addString in interface MatsInitiator.MatsInitiate
      Parameters:
      key - the key on which to store the String payload. The receiver will have to use this key to get the payload out again, so either it will be a specific key that the sender and receiver agree upon, or you could generate a random key, and reference this key as a field in the Request DTO.
      payload - the string.
      Returns:
      the MatsInitiator.MatsInitiate for chaining.
      See Also:
    • request

      public MatsInitiator.MessageReference request(Object requestDto)
      Description copied from interface: MatsInitiator.MatsInitiate
      The standard request initiation method: All of from, to and replyTo must be set. A message is sent to a service, and the reply from that service will come to the specified reply endpointId, typically a terminator.
      Specified by:
      request in interface MatsInitiator.MatsInitiate
      Parameters:
      requestDto - the object which the endpoint will get as its incoming DTO (Data Transfer Object).
    • request

      public MatsInitiator.MessageReference request(Object requestDto, Object initialTargetSto)
      Description copied from interface: MatsInitiator.MatsInitiate
      Variation of the request initiation method, where the incoming state is sent along.

      This only makes sense if the same code base "owns" both the initiation code and the endpoint to which this message is sent. It is mostly here for completeness, since it is possible to send state along with the message, but if employed between different services, it violates the premise that MATS is built on: State is private to the stages of a multi-stage endpoint, and the Request and Reply DTOs are the public interface.

      Specified by:
      request in interface MatsInitiator.MatsInitiate
      Parameters:
      requestDto - the object which the target endpoint will get as its incoming DTO (Data Transfer Object).
      initialTargetSto - the object which the target endpoint will get as its STO (State Transfer Object).
    • send

      public MatsInitiator.MessageReference send(Object messageDto)
      Description copied from interface: MatsInitiator.MatsInitiate
      Sends a message to an endpoint, without expecting any reply ("fire-and-forget"). The 'reply' parameter must not be set.

      Note that the difference between request(..) and send(..) is only that replyTo is not set for send, otherwise the mechanism is exactly the same.

      Specified by:
      send in interface MatsInitiator.MatsInitiate
      Parameters:
      messageDto - the object which the target endpoint will get as its incoming DTO (Data Transfer Object).
    • send

      public MatsInitiator.MessageReference send(Object messageDto, Object initialTargetSto)
      Description copied from interface: MatsInitiator.MatsInitiate
      Variation of the MatsInitiator.MatsInitiate.send(Object) method, where the incoming state is sent along.

      This only makes sense if the same code base "owns" both the initiation code and the endpoint to which this message is sent. It is mostly here for completeness, since it is possible to send state along with the message, but if employed between different services, it violates the premise that MATS is built on: State is private to the stages of a multi-stage endpoint, and the Request and Reply DTOs are the public interface.

      Specified by:
      send in interface MatsInitiator.MatsInitiate
      Parameters:
      messageDto - the object which the target endpoint will get as its incoming DTO (Data Transfer Object).
      initialTargetSto - the object which the target endpoint will get as its STO (State Transfer Object).
    • publish

      public MatsInitiator.MessageReference publish(Object messageDto)
      Description copied from interface: MatsInitiator.MatsInitiate
      Sends a message to a SubscriptionTerminator, employing the publish/subscribe pattern instead of message queues (topic in JMS terms). This means that all of the live servers that are listening to this endpointId will receive the message, and if there are no live servers, then no one will receive it.

      The concurrency of a SubscriptionTerminator is always 1, as it only makes sense for there being only one receiver per server - otherwise it would just mean that all of the active listeners on one server would get the message, per semantics of the pub/sub.

      It is only possible to publish to SubscriptionTerminators as employing publish/subscribe for multi-stage services makes no sense.

      Specified by:
      publish in interface MatsInitiator.MatsInitiate
      Parameters:
      messageDto - the object which the target endpoint will get as its incoming DTO (Data Transfer Object).
    • publish

      public MatsInitiator.MessageReference publish(Object messageDto, Object initialTargetSto)
      Description copied from interface: MatsInitiator.MatsInitiate
      Variation of the MatsInitiator.MatsInitiate.publish(Object) method, where the incoming state is sent along.

      This only makes sense if the same code base "owns" both the initiation code and the endpoint to which this message is sent. The possibility to send state along with the request makes most sense with the publish method: A SubscriptionTerminator is often paired with a Terminator, where the Terminator receives the message, typically a reply from a requested service, along with the state that was sent in the initiation. The terminator does any "needs to be guaranteed to be performed" state changes to e.g. database, and then passes the incoming message - along with the same state it received - on to a SubscriptionTerminator. The SubscriptionTerminator performs any updates of any connected GUI clients, or for any other local states, e.g. invalidation of caches, on all live servers listening to that endpoint, the point being that if no servers are live at that moment, no one will process that message - but at the same time, there is obviously no GUI clients connected, nor are there are local state in form of caches that needs to be invalidated.

      Specified by:
      publish in interface MatsInitiator.MatsInitiate
      Parameters:
      messageDto - the object which the target endpoint will get as its incoming DTO (Data Transfer Object).
      initialTargetSto - the object which the target endpoint will get as its STO (State Transfer Object).
    • unstash

      public <R, S, I> void unstash(byte[] stash, Class<R> replyClass, Class<S> stateClass, Class<I> incomingClass, MatsEndpoint.ProcessLambda<R,S,I> lambda)
      Description copied from interface: MatsInitiator.MatsInitiate
      Unstashes a Mats Flow that have been previously stashed. To be able to deserialize the stashed bytes to instances provided to the supplied MatsEndpoint.ProcessLambda, you need to provide the classes of the original stage's Reply, State and Incoming objects.
      Specified by:
      unstash in interface MatsInitiator.MatsInitiate
      Type Parameters:
      R - type of the ReplyClass
      S - type of the StateClass
      I - type of the IncomingClass
      Parameters:
      stash - the stashed bytes which now should be unstashed
      replyClass - the class which the original stage originally would reply with.
      stateClass - the class which used for state in the original stage (endpoint) - or Void.TYPE (i.e. void.class) if none.
      incomingClass - the class which the original stage gets as incoming DTO.
      lambda - the stage lambda which should now be executed instead of the original stage lambda where stash was invoked.
    • getAttribute

      public <T> Optional<T> getAttribute(Class<T> type, String... name)
      Description copied from interface: MatsInitiator.MatsInitiate
      Provides a way to get hold of (optional) attributes/objects from the Mats implementation, either specific to the Mats implementation in use, or configured into this instance of the Mats implementation. Mirrors the same method at MatsEndpoint.ProcessContext.getAttribute(Class, String...). There is also a ThreadLocal-accessible version at MatsFactory.ContextLocal.getAttribute(Class, String...).

      Mandatory: If the Mats implementation has a transactional SQL Connection, it shall be available by 'context.getAttribute(Connection.class)'.

      Specified by:
      getAttribute in interface MatsInitiator.MatsInitiate
      Type Parameters:
      T - The type of the attribute.
      Parameters:
      type - The expected type of the attribute
      name - The (optional) (hierarchical) name(s) of the attribute.
      Returns:
      Optional of the attribute in question, the optionality pointing out that it depends on the Mats implementation or configuration whether it is available.
      See Also:
    • toString

      public String toString()
      Overrides:
      toString in class Object