The Mats API itself has zero dependencies. The JMS Mats implementation depends on the Mats API, JMS API for messaging, SLF4J for logging, and MatsTrace for wire protocol. In addition, you need an implementation of JMS (ActiveMQ or Artemis client), and an implementation of MatsTrace, the default using Jackson JSON.

Mats is pure Java.

However, if your app is Spring oriented, you might want to define Mats Endpoints using annotations, much like you use @RequestMapping in Spring to define HTTP endpoints.

@EnableMats

And there is a solution for this. This entails annotating some Spring @Configuration class with @EnableMats, which enables bean scanning for Mats-annotations. The @MatsMapping annotation can be added to a method of a Spring Bean, for creating single-stage and Terminator Endpoints. The @MatsClassMapping annotation can be added to a class to define a multi-stage Endpoint.

@MatsMapping

Here’s an example of @MatsMapping to define a single-stage Endpoint:

// Note: MatsFactory must reside in Spring context, and some @Configuration-class
// must have the @EnableMats annotation added.

@Service
class MatsEndpoints {
    
    // possibly inject/autowire stuff here

    @MatsMapping("Service.calculate")
    public ServiceReplyDto springMatsSingleStageEndpoint(ServiceRequestDto msg) {
        // Calculate the resulting values
        double resultNumber = msg.number * 2;
        String resultString = msg.string + ":FromService";
        // Return the reply DTO
        return new ServiceReplyDto(resultNumber, resultString);
    }
}

Here’s the @MatsMapping JavaDoc.

@MatsClassMapping

Here’s an example using @MatsClassMapping to define a two-stage Endpoint.

It is a service calculating some shipping cost, giving free shipping if the customer is one of our special customers. If not, the shipping is rebated if the last year’s total value is > 1000, otherwise standard. To find that last year value, another Mats Endpoint is consulted.

@MatsClassMapping("ShippingService.calculateShipping")
class ShippingEndpointClass {

    // Spring-injected dependency
    private transient ShippingService _shippingService;

    @Autowired
    ShippingEndpointClass(ShippingService shippingService) {
        _shippingService = shippingService;
    }

    // ProcessContext: Injected, or provided as argument to stage methods
    ProcessContext<ShippingCostReply> _context;

    // This field is part of the Endpoint's state
    List<OrderLine> _orderLines;

    @Stage(0)
    void initialStage(ShippingCostRequest msg) {
        // Check if this is one of our special customers
        if (_shippingService.isSpecialCustomer(request.customerId)) {
            // Yes, so he always gets free shipping - reply early.
            _context.reply(ShippingCostReply.freeShipping());
            return;
        }

        // Store the values we need in next stage in state-object ('this').
        _orderLines = msg.orderLines;

        // Perform request to the totalValueLastYear Endpoint...
        _context.request("OrderService.orders.totalValueLastYear",
                new OrderTotalValueRequest(customerId));
    }

    @Stage(1)
    ShippingCostReply calculate(OrderTotalValueReply orderTotalValueReply) {
        // Based on OrderService's response, we'll give rebate or not.
        return orderTotalValueReply.getValue() > 1000
                ? _shippingService.rebated(_orderLines)
                : _shippingService.standard(_orderLines);
    }
}

So, when defining a multi-stage using annotations, you make the stages as separate methods, annotated with @Stage and an ordinal.

Notice how it uses the class itself for state, while still being able to have Spring Beans injected. This variant also has the ProcessContext injected, but that can also be passed in with the arguments for the stage methods, simplifying testing. Depending on your acceptance of “magic”, this can be a bit head-twisting, but in actual usage it feels pretty natural.

It is explained more thoroughly in Endpoints, Stages and Initiations, and the @MatsClassMapping JavaDoc

So, Mats has an integration with Spring, which is great for developers. But there’s more, and this stuff also needs to run in production! Over to the next chapter (Or go to explore!)

Updated: