Search code examples
spring-websocketactivemq-artemisspring-messaging

Integrating Spring controllers message mapping and Artemis


I'm researching switching to WildFly 10 and, subsequently, Artemis. I've set up a simple Spring Websocket project, as described here: https://spring.io/guides/gs/messaging-stomp-websocket/. The meat of the project are WebSocketConfig:

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableStompBrokerRelay("/queue/", "/topic/");
    config.setApplicationDestinationPrefixes("/app");
}

And controller:

@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
    return new Greeting("Hello, " + message.getName() + "!");
}

This works like a charm as it is with ActiveMQ, no additional configuration required on the broker side.

Knowing that Artemis requires either a pre-created destination, or a specific mention of automatic queue creation, i added this to my broker.xml:

<address-setting match="#">
    <dead-letter-address>jms.queue.DLQ</dead-letter-address>
    <expiry-address>jms.queue.ExpiryQueue</expiry-address>
    <redelivery-delay>0</redelivery-delay>
    <max-size-bytes>10485760</max-size-bytes>
    <message-counter-history-day-limit>10</message-counter-history-day-limit>
    <address-full-policy>BLOCK</address-full-policy>
    <auto-create-jms-queues>true</auto-create-jms-queues>
    </address-setting>
</address-settings>

However, this didn't work as i expected and broker still refused to create /queue/greetings. So, i've researched a bit more and found that Artemis, in fact, likes queues to be names with jms.queue.* prefix. I renamed my queue to jms.queue.greetings and made the following changes to Spring code: Configuration:

...
config.setPathMatcher(new AntPathMatcher("."));
...

Controller:

...
@SendTo("jms.queue.greetings")
...

To my understanding, this should have forced my controller to send messages to this queue. Broker liked this change and finally created the queue, but Spring controller on the other hand did not - no messages appear in jms.queue.greetings.

I can force my way around the issue by making code less pretty, subscribing to required queues with some hand-made injected services, but that way i will loose Spring sockJS magic - Artemis (unlike RabbitMQ) doesn't seem to provide http endpoint to Stomp, and connecting to ws:// will just lead to CORS errors. No inbuilt CORS support either, it seems.

I will appreciate any thoughts on how i might overcome this issue.

EDIT: My JS looks like this:

var ws = new SockJS('/hello');
client = Stomp.over(ws);
...
client.connect(login, passcode, function(frame) {
    client.debug("connected to Stomp");
    ....
    client.subscribe('jms.queue.greetings', function(message) {
        ...
    });
});
...
client.send("jms.queue.greetings", {}, JSON.stringify({ 'name': text }));
...

Solution

  • Based on your update

    When sending to jms.queue.greeting you are telling spring to redirect your request directly to Broker without entering any controller if you want to send message to controller and then it forward it to broker you should send per your configuration to app.greeting and that controller will forward it back to jms.queue.greeting

    if you are using . as delimiter use it in all annotation

    check this diagram and it will show you the difference between sending to /app and /queue or /topic the first go into controller the second is only redirect

    I am using the below and i am able to send and receive message

    //Configuration
        @Override
            public void configureMessageBroker(MessageBrokerRegistry config) {
                config.enableStompBrokerRelay("jms.topic", "jms.queue")
                .setRelayHost("192.168.65.24")
                .setRelayPort(5445)
                .setSystemLogin("root")
                .setSystemPasscode("XXXXX")
                .setClientLogin("root")
                .setClientPasscode("XXXXX");
                config.setApplicationDestinationPrefixes("app");
                //config.setUserDestinationPrefix("user"); 
                config.setPathMatcher(new AntPathMatcher("."));
            }
    
            @Override
            public void registerStompEndpoints(StompEndpointRegistry registry) {
                registry.addEndpoint("/stomp");
            }
    
    
    //Subscribtion
         stompClient.subscribe('jms.queue.call', function(greeting){
                            showGreeting(JSON.parse(greeting.body).name);
                        });
    
    //Sending
        stompClient.send("jms.queue.call", {}, JSON.stringify({ 'name': message }));
    

    One issue still pending with artemis is the /user/ where DefaultUserDestinationResolver is hardcoded with / for user parsing .