Search code examples
javajmswebsphere-libertyopen-libertyjava-ee-8

How to configure OpenLiberty 18.0.0.2 to use Liberty embedded messaging?


I am trying to configure the OpenLiberty 18.0.0.2 to use embedded messaging for sending some simple JMS messages.

My current server.xml looks like the following:

<?xml version="1.0" encoding="UTF-8"?>

<server description="new server">
  <featureManager>
    <feature>javaee-8.0</feature>
    <feature>mpConfig-1.2</feature>
    <feature>mpMetrics-1.1</feature>
    <feature>wasJmsServer-1.0</feature>
    <feature>wasJmsClient-2.0</feature>
    <feature>localConnector-1.0</feature>
  </featureManager>
  <quickStartSecurity userName="admin" userPassword="adminpwd" />

  <httpEndpoint id="defaultHttpEndpoint" httpPort="9080" httpsPort="9443" />

  <applicationManager autoExpand="true" />
  <applicationMonitor updateTrigger="mbean" />

  <messagingEngine>
    <queue id="QUEUE1" />
  </messagingEngine>

  <jmsQueueConnectionFactory jndiName="jms/JmsFactory">
    <properties.wasJms remoteServerAddress="localhost:7276:BootStrapBasicMessaging" />
  </jmsQueueConnectionFactory>

  <jmsQueue jndiName="jms/JmsQueue">
    <properties.wasJms queueName="QUEUE1" />
  </jmsQueue>
</server>

And my JMS sender looks like the following:

public class JmsMessageSender {


    @Resource(mappedName = "jms/JmsFactory")
    private ConnectionFactory jmsFactory;

    @Resource(mappedName = "jms/JmsQueue")
    private Queue jmsQueue;

    public void send() {

        TextMessage message;

        try (Connection connection = jmsFactory.createConnection();
             Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
             MessageProducer producer = session.createProducer(jmsQueue)) {

            message = session.createTextMessage();
            message.setText("Hello World!");
            producer.send(message);

        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

When I am running my application I get the following exception when I am trying to send the message to the embedded messaging queue:

javax.jms.InvalidDestinationException: CWSIA0281E: The specified value null is not allowed for Destination.
[err]   at com.ibm.ws.sib.api.jms.impl.JmsDestinationImpl.checkNativeInstance(JmsDestinationImpl.java:993)
[err]   at [internal classes]

It looks like my code can't obtain the queue destination through JNDI. Did I configure embedded messaging incorrectly or is the error in my source code?

UPDATE 1:

I updated the source code so that I won't pass the destination to the .send() method an now I am getting the following error on startup:

[ERROR   ] cdi.resource.injection.error.CWOWB1000E                                                                                                           
CWNEN0030E: The server was unable to obtain an object instance for the java:comp/env/de.rieckpil.blog.JmsMessageSender/jmsQueue reference.  
The exception message was: CWNEN1004E: The server was unable to find the de.rieckpil.blog.JmsMessageSender/jmsQueue default binding with the javax.jms.Queue type for the java:comp/env/de.rieckpil.blog.JmsMessageSender/jmsQueue reference.

UPDATE 2:

Sending the message now works, but I am unable to receive the message. My message driven bean looks like the following (the feature mdb-3.2 is enabled):

@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "destination",
                propertyValue = "jms/JmsQueue"),
        @ActivationConfigProperty(propertyName = "destinationType",
                propertyValue = "javax.jms.Queue")
})
public class JmsMessageReader implements MessageListener {

    @Override
    public void onMessage(Message message) {

        TextMessage textMessage = (TextMessage) message;
        try {
            System.out.println("Message arrived: " + textMessage.getText());
        } catch (JMSException e) {
            System.err.println(e.getMessage());
        }

    }
}

Solution

  • EDIT: Update after comment that there was an issue with resource injection

    First, fix the JMS API usage to only pass in the destination once (not on both createProducer() and send(). Otherwise you may experience a CWSIA0066E failure.

    Second, change the @Resource attributed from mappedName to lookup

    These changes are reflected below:

    @Resource(lookup = "jms/JmsFactory")
    private ConnectionFactory jmsFactory;
    
    @Resource(lookup = "jms/JmsQueue")
    private Queue jmsQueue;
    
    public void send() {
    
        TextMessage message;
    
        try (Connection connection = jmsFactory.createConnection();
             Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
             MessageProducer producer = session.createProducer(jmsQueue)) {
    
            message = session.createTextMessage();
            message.setText("Hello World!");
    
            // Don't pass in destination again since you set it in createProducer()
            producer.send(message);   
    
            // ...
    

    @Resource injection

    Finally, @Resource injection isn't going to work in any old POJO, but only in special classes that are scanned by the container. Try moving the injection to a servlet, EJB, or CDI managed bean.

    Terminology note:

    Though I can see why you would have thought to call this "embedded MQ", the "messaging engine" you're using here is provided by what is actually called Liberty embedded messaging.

    It is also a JMS provider. That is, it implements the JMS API, as does MQ, the other IBM-provided JMS provider that can be used in Liberty.