Hot questions for Using GlassFish in jms

Question:

I have 2 projects with same 'Chatter.java' files in. I'd like to chat between programs. But only one of them can send messages to other one. Here is how it looks like Tim can write to Xerox, but if Xerox try to send he gets a

javax.jms.MessageNotWriteableException: [C4008]: Message in read-only mode.

I'm using GlassFish server with settings:

So, and my code is

package aero;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jms.Connection;
import javax.jms.Queue;
import javax.annotation.Resource;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class Chatter extends JFrame implements Runnable{
    @Resource(mappedName = "aeroPool")
    private static ConnectionFactory connectionFactory;
    @Resource(mappedName = "aeroJNDI")
    private static Queue queue;

    private JTextArea msgArea = new JTextArea();
    private JTextField input = new JTextField("Message");
    private JButton button = new JButton("Send");   
    private JPanel lowerPanel = new JPanel(new GridLayout(2, 1));

    MessageConsumer consumer = null;
    Message message = null;
    MapMessage mapMessage = null;
    Connection queueConnection = null;
    Session session = null;
    MessageProducer producer = null;

    Thread th = null;

    String nickname = "Xerox";

    public Chatter(){
        connect();
        initGUI();
        th = new Thread(this);
        th.start();
    }

    public void initGUI(){
        this.setLayout(new BorderLayout());
        this.setTitle("Chat - " + nickname);
        this.setSize(300, 100);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(msgArea, BorderLayout.CENTER);
        lowerPanel.add(input);
        lowerPanel.add(button);
        this.add(lowerPanel, BorderLayout.SOUTH);

        button.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                try {    
                    sendMessage();
                } catch (JMSException ex) {
                    Logger.getLogger(Chatter.this.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }

    public void connect(){
        try {
            queueConnection = connectionFactory.createConnection();
            session = queueConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            producer = session.createProducer(queue);
            mapMessage = session.createMapMessage();
            consumer = session.createConsumer(queue);
            queueConnection.start();
        } catch (JMSException ex) {
            Logger.getLogger(Chatter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void sendMessage() throws JMSException{  
        Date date = new Date(mapMessage.getJMSTimestamp());
        String time = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
        String textMessage = input.getText();
        mapMessage.setString("Time", time);
        mapMessage.setString("Nickname", nickname);
        mapMessage.setString("Message", textMessage);
        producer.send(mapMessage);
        msgArea.append(System.lineSeparator() + nickname + " @ " + time + " - " + textMessage);
    }

    public void run(){
        while(true){
            try {
                th.sleep(500);
                getMessage();
            } catch (InterruptedException ex) {
                Logger.getLogger(this.getName()).log(Level.SEVERE, null, ex);
            } catch (JMSException ex) {
                Logger.getLogger(this.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void getMessage() throws JMSException{
        do{
            message = consumer.receive(10000);                
            if(message != null){
                if(message instanceof MapMessage){
                    mapMessage = (MapMessage) message;
                    if(!mapMessage.getString("Nickname").equals(nickname)){
                        String msg = mapMessage.getString("Nickname");
                        msg += " @ ";
                        msg += mapMessage.getString("Time");
                        msg += " - ";
                        msg += mapMessage.getString("Message");
                        msgArea.append(System.lineSeparator() + msg);
                    }
                }
            }
        }while(message != null);  
    }

    public static void main(String[] args) {
        new Chatter().setVisible(true);
    }
}

Answer:

Your sendMessage() method uses a field called mapMessage. The problem is mapMessage also gets written by getMessage(). In most JMS implementations received messages cannot be modified.

You should eliminate the mapMessage field all together and replace it with a local variable in each of sendMessage() and getMessage(). Create a new message via session.createMapMessage() every time you want to send a message. Likewise when you receive a message you should read the message contents and then discard it.

Question:

I am developing an application which used JMS as the messaging layer. I'm also using glassfish to host the jms/mq backend. The application is able to do pub/sub messaging using a connection factory and topic in the glassfish 3.1 server that I originally set up. I now have another instance of glassfish (4.1) that hosts a new set of functionality that a new suite of applications uses, but I still need to consume the messages broadcast by the first glassfish server. The fact that the clients use new libraries specific to glassfish 4.1, I can't connect directly to the glassfish1 server.

I have followed this tutorial regarding multi-server environments (https://docs.oracle.com/cd/E19798-01/821-1841/bncfp/index.html) and the stand-alone java clients all use the connection factory set up in the new glassfish server to connect to the old glassfish server. I know the connection is being made because if I stop glassfish1, I get connection dropped errors, etc.

The relevant client code is as follows:

        System.setProperty("org.omg.CORBA.ORBInitialHost", "10.20.10.52");
        System.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
        try {
            try {
                ctx = new InitialContext();
        } catch (NamingException ex) {
            ex.printStackTrace();
        }
        ConnectionFactory cf = (ConnectionFactory) ctx.lookup("jms/ConnectionFactory");
        Connection connection = cf.createConnection();
        jmsContext = cf.createContext(Session.AUTO_ACKNOWLEDGE);
        topic = (Topic) ctx.lookup("jms/Topic");
        updateShipperConsumer = jmsContext.createConsumer(topic);
        jmsProducer = jmsContext.createProducer();

        logger.info("Started JMS successfully!");
    } catch (NamingException ex) {
        ex.printStackTrace();
    } catch (JMSException ex) {
        Logger.getLogger(LamtecJMSSystemImpl.class.getName()).log(Level.SEVERE, null, ex);
    }
 }

The jms/ConnectionFactory jndi is a local connection factory on glassfish2 that has an AddressList property set to glassfish1:7676. There is a corresponding connection factory on glassfish1 with the same name, as suggested by the tutorial. Looking at the imq log files on the glassfish1 server, I see that the connection from glassfish2 is made. I'm not sure I should be doing a lookup on the topic jndi (which I have locally, as well as on the remote server), but I don't think that has made a difference.

According to the tutorial that I referenced above, I have done everything needed configuration and code-wise, but I still don't get any jms messages in my clients. Any ideas?


Answer:

I can advise about JMS queues, probably for topics is similar or same solution. You can create queues with same names on both servers, and let the first queue push its messages to the second one using glassfish configuration, so let the imq brokers do the job. Configure the jms connection factory on the first server with the property "AddressList" with value "mq://host2:port2" with the settings for the second broker.

See the glassfish resource template for such a configuration in the OSCM Service Catalog https://github.com/servicecatalog/development/blob/master/oscm-installation/domains/bes_domain/installer/resources-template.xml

The example is the connection factory "jms/bss/masterIndexerQueueFactory" which you can find in this template.

More about the Open Source Project OSCM Cloud Service Management Software

Question:

Having a custom object like this:

package Messages;

import java.io.Serializable;
import javax.jms.TextMessage;

public class MessageObject implements Serializable{

private static final long serialVersionUID = 1L;
private TextMessage message;
private String state; 
private String sender;
private String receiver;

public MessageObject() {

}

public TextMessage getMessage() {
    return message;
}

public void setMessage(TextMessage message) {
    this.message = message;
}
* the rest of the getters and setters *

}

Then I create I try to send to my jms queue an ObjectMessage containing my custom message object:

try {
        Context context = new InitialContext();

        QueueConnectionFactory factory = 
        (QueueConnectionFactory)context.lookup("tiwconnectionfactory");
        Queue queue = (Queue) context.lookup("tiwqueue");
        Connection connection = factory.createConnection();
        connection.start(); 
        Session session= connection.createSession(false, 
        javax.jms.Session.AUTO_ACKNOWLEDGE);

        MessageProducer producer = session.createProducer(queue);


        MessageObject mymessage = new MessageObject();


         ObjectMessage object = session.createObjectMessage();


        TextMessage message = session.createTextMessage();
        message.setText("Hi");


        mymessage.setState("still not read");
        mymessage.setMessage(message);
        mymessage.setSender("Peter");
        mymessage.setReceiver("John");


        object.setObject(mymessage);


        producer.send(object);  

When trying to run the code, exception throwed is:

[2018-11-11T19:20:07.270+0100] [glassfish 5.0] [SEVERE] [] [] [tid: _ThreadID=28 _ThreadName=Thread-9] [timeMillis: 1541960407270] [levelValue: 1000] [[ javax.jms.JMSException:MQJMSRA_DM4001::Exception:ObjectMessage.setObject()object=Messages.MessageObject@d8b66e3:message=com.sun.messaging.jms.ra.DirectTextPacktatcom.sun.messaging.jms.ra.DirectObjectPacket.setObject(DirectObjectPacket.java:218)

So what the problem is? I dont find any errors in my code, and my object is serializable.


Answer:

I used google to pick apart your error message:

[2018-11-11T19:20:07.270+0100] [glassfish 5.0] [SEVERE] [] [] [tid: _ThreadID=28 _ThreadName=Thread-9] [timeMillis: 1541960407270] [levelValue: 1000] 
[[ javax.jms.JMSException:MQJMSRA_DM4001::Exception:ObjectMessage.setObject()
  object=Messages.MessageObject@d8b66e3:
  message=com.sun.messaging.jms.ra.DirectTextPacket
  at com.sun.messaging.jms.ra.DirectObjectPacket.setObject(DirectObjectPacket.java:218)

It seems that the TextMessage object is actually an instance of com.sun.messaging.jms.ra.DirectTextPacket.

However, for your MessageObject to be serializable all of its fields need to be serializable too. As DirectTextPacket is not serializable, your MessageObject is not serializable.


To answer the question "why do you get this exception instead of NotSerializableException"?

This is because JMS wraps exceptions like NotSerializableException into its own exceptions.


What I do not understand: why do you want your MessageObject to contain a wrapped TextMessage? Why not add the text itself into your MessageObject?

Question:

I am using Glassfish and OpenMQ for a remote Consumer instance to look up a message Producer's queue and process requests synchronously.

According to McIntosh's answer on Synchronous Consumer with JMS Queue, synchronous message receiving can be handled via scheduling. I plan on doing this, but I have only seen examples of connecting to a message queue by way of an asynchronous Message Driven Bean (MDB), as shown below:

import javax.jms.MessageListener;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;

@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "addressList", propertyValue = "mq://localhost:54020/"), //found in Producer server's domain.xml as JMS_PROVIDER_PORT
@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/ProducerRequestMessageQueue"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")})

public class ConsumerNode extends Node implements MessageListener  {

    @Resource
    private MessageDrivenContext _mdc;

    public ConsumerNode() {
        super();
    }

    @Override
    public void onMessage(Message message) {
        //process message...
    }
}

How do I connect to the remote Producer queue without implementing MessageListener and setting up as a Message Driven Bean?


Answer:

Finally figured it out. I was needing the imq.jar mq library located at:

%GLASSFISH_HOME%/mq/lib/

The following code answers my question with core pieces being the init() and onMessage() methods:

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Schedule;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.ObjectMessage;
import javax.jms.StreamMessage;
import javax.jms.TextMessage;
import javax.jms.Connection;
import javax.jms.Queue;

//from imq.jar
import com.sun.messaging.ConnectionConfiguration;
import com.sun.messaging.ConnectionFactory;

@Singleton
public class SyncNode {
    private ConnectionFactory _producerRequestFactory;
    private Connection _connection;
    private Session _session;
    private Queue _producerRequestMessageQueue;
    private MessageConsumer _consumer;

    @PostConstruct
    void init() {
        try {
            _producerRequestFactory = new ConnectionFactory();
            _producerRequestFactory.setProperty(ConnectionConfiguration.imqBrokerHostName, "localhost");
            _producerRequestFactory.setProperty(ConnectionConfiguration.imqBrokerHostPort, "56527"); //56527 is JMS_PROVIDER_PORT found in producer's domain.xml in domain config directory
            _connection = _producerRequestFactory.createConnection();
            _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            _producerRequestMessageQueue = _session.createQueue("ProducerRequestMessageQueue"); //name of the queue that the producer sends messages to.
            _consumer = _session.createConsumer(_producerRequestMessageQueue);
            _connection.start();
        } catch (JMSException ex) {
            //handle exception
        }
    }

    @PreDestroy
    void cleanup() {
        try {
            _consumer.close();
            _session.close();
            _connection.close();
        } catch (JMSException ex) {
            //handle exception
        }
    }

    @Schedule(hour = "*", minute = "*", second = "*/10", persistent = false)
    public void onMessage() {
        try {
            _connection.start(); 
            Message message = _consumer.receive();
            //handle message
        } catch (JMSException ex) {
           //handle exception
        }
    }
}

Two code examples helped me:

  1. This answer taught me the proper annotations to escape using the main method.
  2. This tutorial helped make things click on how to connect to another server, or port in my case.

Question:

I'm using Glassfish 3.1.2.2 with OpemMQ 4.5.2 (remote mode). In my application, there is a subscriber of a JMS topic, that receives messages asynchronously, using a MessageListener.

When broker is stopped and started after a while, messages published to the JMS topic after restart are not received by the subscriber.

What is the best way to recover the topic subscriber?

I've tried ExceptionListener, but apparently it's not supported when running my application on Glassfish.


Answer:

Changed "Reconnect Attempts" option from "Java Message Service" to "-1" and Glassfish successfully reconnected publisher and subscriber.

Question:


Answer:

GlassFish in production perfectly works.

And if your boss wants to buy support and patches for it have a look at http://www.payara.co.uk/about