0

I have a stateful local session bean in one ear and a mbean in another ear both deployed on the same WildFly 27 standalone-full instance.

I have a topic set up in standalone-full.xml:

<jms-topic name="topic/enterpriseYTopic" entries="java:/jms/topic/enterpriseYTopic"/>

enter image description here

I have a stateful local bean with this contents. When a user is not found an exception is being thrown and one(!) message sent to the topic queue

@Stateful
@LocalBean
public class UserService {
    
    @Resource(mappedName = "java:/jms/topic/enterpriseYTopic")
    private Topic topic;
    
    @Inject
    JMSContext context;
        
    public Map<String, String> findByUserName(String username) {
        try {
            Map<String, String> user = userList.get(username);
            if(user!=null) {
                return user;
            }
        throw new Exception("Invalid access: " + username + " does not exist");
        } catch (Exception e) {
            // send to topic with filter params
            getProducer().send(topic, getMessage("Access violation at login: " + 
                username, "department", "security-department"));
        return null;
        }
    }
    
    private ObjectMessage getMessage(String message, String propertyName, String propertyValue) {
    ObjectMessage om = context.createObjectMessage(message);
    try {
        om.setStringProperty(propertyName, propertyValue);
    } catch (JMSException e) {
        e.printStackTrace();
    }
    return om;
    }
    
    private JMSProducer getProducer() {
        return context.createProducer();
    }
}

I have a MDB with this contents. The message's string property is compared to the RECIPIENT value. If there is a match the processMessage() gets called in the super class MessageHandler.

@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "java:/jms/topic/enterpriseYTopic"),
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "jakarta.jms.Topic") 
})
public class TopicSubscriberSecurityDepartment extends MessageHandler implements MessageListener {
    
    private static final Logger logger = LogManager.getLogger(TopicSubscriberSecurityDepartment.class);

    public static final String RECIPIENT = "security-department";
    
    
    protected void processMessage(Message message) {
        try {
            logger.info("Security-Department received Message: " + ((ObjectMessage)message).getObject().toString());
        } catch (Exception e) {
            logger.error(e);
        }
    }
    
    @Override
    boolean isValidRecipient(Message message) throws JMSException {
        final boolean state[] = {false};
        Arrays.asList(message.getStringProperty(RECIPIENT_KEY).split("\\|")).forEach(r -> {
            if (r.equals(RECIPIENT)) {
                state[0] = true;
            }
        });
        return state[0];
    }
}

MessageHandler code:

public abstract class MessageHandler {

    public static final String RECIPIENT_KEY = "department";
    
    public void onMessage(Message message) {
        try {
                // in case the key 'department' is found the deriving class' implementation 
                // of isValidRecipienrt() checks whether this is the right department 
                // and prints out the message 
            if (message.getStringProperty(RECIPIENT_KEY)!=null) {
                if (isValidRecipient(message)) {
                    processMessage(message);
                    message.acknowledge();
                } 
                return;
            } else {
                        // if the property does not exist print the message anyway
                processMessage(message);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    abstract boolean isValidRecipient(Message message) throws JMSException;
    abstract void processMessage(Message message);
}

When I login with wrong credentials I'm seeing this in the eclipse console, although the message should have been sent only once. there obviously are two threads 261 and 256 which cause this but I'm not able to figure out why.

15:30:20,936 INFO  [cs.edu.ey.jms.TopicSubscriberSecurityDepartment] (Thread-261 (ActiveMQ-client-global-threads)) Security-Department received Message: Access violation at login: admhjin
15:30:20,936 INFO  [cs.edu.ey.jms.TopicSubscriberSecurityDepartment] (Thread-256 (ActiveMQ-client-global-threads)) Security-Department received Message: Access violation at login: admhjin

When starting WildFly I see that this MBean is started twice

16:00:04,390 INFO  [org.jboss.as.ejb3] (MSC service thread 1-5) WFLYEJB0042: Started message driven bean 'TopicSubscriberSecurityDepartment' with 'activemq-ra.rar' resource adapter
16:00:04,391 INFO  [org.jboss.as.connector.deployment] (MSC service thread 1-1) WFLYJCA0118: Binding connection factory named java:/JmsXA to alias java:jboss/DefaultJMSConnectionFactory
16:00:04,391 INFO  [org.jboss.as.ejb3] (MSC service thread 1-2) WFLYEJB0042: Started message driven bean 'TopicSubscriberSecurityDepartment' with 'activemq-ra.rar' resource adapter

Otherwise there are no errors in the console, but it seems somehow related to the configuration, but I just can't figure out what the issue is. So if anyone could give a clue thanks heaps :)

Edit: I found a clue and a fix for the issue but still do not understand it. I changed the message format from Object to TextMessage and made a cast to TextMessage in the MessageListener. The first message works fine but the second message then throws this exception:

18:23:38,451 INFO  [cs.edu.ey.jms.TopicSubscriberSecurityDepartment] (Thread-100 (ActiveMQ-client-global-threads)) Security-Department received Message: Access violation at login: adklmin
18:23:38,453 ERROR [cs.edu.ey.jms.TopicSubscriberSecurityDepartment] (Thread-87 (ActiveMQ-client-global-threads)) java.lang.ClassCastException: class org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQObjectCompatibleMessage cannot be cast to class jakarta.jms.TextMessage (org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQObjectCompatibleMessage is in unnamed module of loader 'org.apache.activemq.artemis@2.26.0' @7916b053; jakarta.jms.TextMessage is in unnamed module of loader 'jakarta.jms.api@3.1.0' @7f61611f): java.lang.ClassCastException: class org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQObjectCompatibleMessage cannot be cast to class jakarta.jms.TextMessage (org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQObjectCompatibleMessage is in unnamed module of loader 'org.apache.activemq.artemis@2.26.0' @7916b053; jakarta.jms.TextMessage is in unnamed module of loader 'jakarta.jms.api@3.1.0' @7f61611f)

The second message(in the second thread) is of type org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQObjectCompatibleMessage obviously a different type from the TextMessage that I'm sending. Why ActiveMQ Artemis is doing this - no clue. But I can work around my error by checking the type of the message before processing it. Still would like to know what's going on there.

Edit: I further investigated into this. The issue totally resolves when the message producer sits in the same EAR as the listening MBean. I had them sitting in two different EARs, on the same wildfly instance. Still I don't get it why two threads are being spawned containing identical messages, including the message-id. Two threads ok, but why two identical messages? It seems the sending process is 100% copied to the other EAR. I tried to find any hints in the doc but nope. I mean, what happens when you send a message to a remote EAR? Should create the same issue. Still hoping someone has a clue :)

ranxero
  • 11
  • 1
  • 4

1 Answers1

0

I confess I have not understood the details of your workarounds and further findings. But I assume what happens here is, each thread creates its own subscription to the topic, thus both of them get the same set of messages. Try setting property "clientId" to a meaningful identifier in ActivationConfigProperty. This should let both threads use the same subscription.

Nine Friends
  • 156
  • 5
  • yes, that is exactly what happens but only if sender and receiver sit in two different EARs.(on the same wildfly instance). The "workaround" which is not such, is to put sender & receiver in the same EAR, which makes no sense from perspective of architecture. Still, why two threads with identical messages if there is only one sender and one comsumer. Anyway thanks for the idea. I will try it out. – ranxero Jun 20 '23 at 19:04
  • Did setting property "clientId" to the same value in the both threads solve your problem? – Nine Friends Aug 18 '23 at 12:39
  • thanks for your reply ... no, I tried that but it didn't resolve the issue. I guess it has somehow to do with sending the message from a session bean. In this case a stateful one. I changed the architecture already to send the message directly from the rest service where the login happens and that fixed the issue. – ranxero Aug 24 '23 at 16:12