Search code examples
tomcatjmsjndiatmospheretibco-ems

NullPointerException from Catalina when starting Tomcat with JMS Tibco EMS JNDI resources


in my Primefaces application I want to use atmosphere framework 's JMSBroadcaster in order to propagate messages between 2 instances of my application.

To do that, I need to lookup (JNDI) for JMS ConnectionFactory and Topic (the implementation we are using is Tibco EMS).

I am very new to JMS and I cannot figure how to configure a Tibco JMS resource in Tomcat's context.xml and server.xml (I am not using active MQ). I am trying to declare these JMS resources from EMS in Tomcat 8. I added tibjms-7.0.1.jar and jboss-jms-api_1.1_spec-1.0.1.Final.jar in tomcat8/lib/

But, at tomcat startup, I get the following error (even without any war deployed) :

SEVERE [main] org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans RuntimeException java.lang.NullPointerException

I cannot figure out how where is the problem (I am not sure I can have more details about this logged error).

Here is my tomcat configuration :

In server.xml

<Resource 
    id="atmosphereFactory" 
    name="jms/atmosphereFactory"   
    jndiName="atmosphereFactory"  
    auth="Container"  
    type="com.tibco.tibjms.naming.TibjmsInitialContextFactory"  
    factory="com.tibco.tibjms.naming.TibjmsObjectFactory"  
    factoryClass="com.tibco.tibjms.naming.TibjmsInitialContextFactory"  
    brokerName="localhost"  
    brokerURL="tcp://localhost:7222"  
    serverUrl="localhost:7222"  
    userName="admin"  
    password="" />
<Resource 
    id="atmosphere" 
    name="jms/atmosphere/test.atmo"
    jndiName="atmosphere"
    auth="Container"
    type="com.tibco.tibjms.TibjmsTopic" 
    factory="com.tibco.tibjms.naming.TibjmsObjectFactory"
    physicalName="test.atmo"/>

In context.xml

<ResourceLink 
    global="jms/atmosphereFactory" 
    name="jms/atmosphereFactory" 
    type="com.tibco.tibjms.naming.TibjmsInitialContextFactory" />
<ResourceLink 
    global="jms/atmosphere" 
    name="jms/atmosphere" 
    type="com.tibco.tibjms.TibjmsTopic" />

Alternatively, I might also be interested in a way to configure atmosphere's JMSBroadcaster using ConnectionFactory and Topic injected from Spring.


Solution

  • I actually used Spring to instanciate Jms Topic...

    <!-- Connection to Tibco EMS -->
    <bean id="tibjmsConnectionFactory" class="com.tibco.tibjms.TibjmsConnectionFactory">
        <property name="serverUrl" value="${instance.jms.server}" />
        <property name="userName" value="${instance.jms.login}"/>
        <property name="userPassword" value="${instance.jms.password}"/>
    </bean>
    
    <bean id="jmsTemplateEms" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="tibjmsConnectionFactory" />
        <property name="explicitQosEnabled" value="true" />
        <property name="deliveryMode" value="2" />
        <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
        <property name="sessionTransacted" value="false" />
        <property name="receiveTimeout" value="${instance.wait.timeout}" />
    </bean>
    
    <bean id="pushJmsMessageListener" class="com.agipi.g2a.tiana.web.utils.PushJmsMessageListener" />
    
    <bean id="atmosphereTopic" class="com.tibco.tibjms.TibjmsTopic">
        <!-- nom du topic-->
        <constructor-arg index="0" value="${instance.jms.atmosphere.topic.name}" />
    </bean>
    <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="tibjmsConnectionFactory"/>
        <property name="destination" ref="atmosphereTopic"/>
        <property name="messageListener" ref="pushJmsMessageListener" />
    </bean>
    

    ... and created a MessageListener which listen to atmosphere topic and publish received message to Push event bus ...

    class PushJmsMessageListener implements MessageListener {
        private static final Logger LOGGER = LoggerFactory.getLogger(PushJmsMessageListener.class);
    
        private static final String PROPERTY_PUSH_CHANNEL = "pushChannel";
    
        @Override
        public void onMessage(Message message) {
    
            try {
                EventBus eventBus = EventBusFactory.getDefault().eventBus();
                TextMessage testMessage = (TextMessage) message;
                LOGGER.info("Reception d'un message du topic atmosphere : {}", testMessage);
    
                String canal = testMessage.getStringProperty(PROPERTY_PUSH_CHANNEL);
                Object decodedObject = new JSONDecoder().decode(testMessage.getText());
                LOGGER.info("Envoi du message sur le endpoint push [canal={},objet={}]", canal, decodedObject);
                eventBus.publish(canal, decodedObject);
            } catch (JMSException e) {
                LOGGER.error("error.receiving.jms.message", e);
    
            }
        }
    }
    

    ... and created a Spring service to publish my message to the topic instead of push ...

    @Service
    @Scope(value = "singleton")
    public class JmsAtmosphereServiceImpl implements JmsAtmosphereService {
    
        @Autowired
        @Qualifier("jmsTemplateEms")
        private JmsTemplate jmsTemplate;
    
        @Autowired
        @Qualifier("atmosphereTopic")
        private Topic atmosphereTopic;
    
    
        @Override
        public void sendMessage(String pushChannel, String jsonContent) {
            jmsTemplate.send(atmosphereTopic, session -> {
                TextMessage textMessage = session.createTextMessage(jsonContent);
                textMessage.setStringProperty("pushChannel", pushChannel);
                return textMessage;
            });
    
        }
    
    }
    

    ... some utility to abstract ...

    @Service
    public class PushJmsUtils {
    
        private static final String PUSH_DEFAULT_CHANNEL = "atmosphere";
    
        @Autowired
        private JmsAtmosphereService jmsAtmosphereService;
    
        /**
         * Propagate message JMS as JSON to JMS Atmosphere topic.
         *
         * @param channel push channel
         * @param message object to send via push
         */
        public void propagateMessage(String channel, Object message) {
            String id = channel;
            if (id.startsWith("/*")) {
                id = PUSH_DEFAULT_CHANNEL;
            }
            jmsAtmosphereService.sendMessage(id, new JSONEncoder().encode(message));
        }
    
    }
    

    .. and then, I publish my message to several instances of my application listening to the same atmosphere topic (including the application sending the message).

     pushJmsUtils.propagateMessage(canal,pushMessage);