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"/>
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 :)