Domanda

I am trying to setup a route in Camel that reads messages from a JMS queue.

The application uses Tibco and I am not allowed to post here any of the data, but the path for the factory and the queue follows the format /path/to/queueName:type, where type can be qcf (Queue Connection Factory) and queue.

I am using the Spring-DSL, the XML is:

<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
    <property name="environment">
        <props>
            <prop key="java.naming.factory.initial">...</prop>
            <prop key="java.naming.provider.url">...LDAP Server URL...</prop>
            <prop key="java.naming.referral">follow</prop>
            <prop key="java.naming.security.credentials">...</prop>
            <prop key="java.naming.security.principal">uid=...,ou=...,dc=...,dc=...</prop>
        </props>
    </property>
</bean>

<bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiTemplate" ref="jndiTemplate"/>
    <property name="jndiName" value="/path/to/queueConnectionFactory:qcf"/>
</bean>

<bean id="authenticatedConnectionFactory" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
    <property name="targetConnectionFactory" ref="jmsQueueConnectionFactory"/>
    <property name="username" value="..."/>
    <property name="password" value="..."/>
</bean>

<bean id="testjms" class="org.apache.camel.component.jms.JmsComponent"> 
    <property name="connectionFactory" ref="authenticatedConnectionFactory"/> 
</bean>

<camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring">
    <route id="jmsRouteTest">
        <from uri="testjms:queue:/path/to/queue:queue" />
        <to uri="file:c:\inbox?fileName=jmsMessage.txt" />
    </route>
</camelContext>

While the application is running it keeps throwing the following lines in the log:

12:41:19:385 - WARN - DefaultMessageListenerContainer - Setup of JMS message listener invoker failed for destination 'path/to/queue:queue' - trying to recover. Cause: Not allowed to create destination
12:41:20:494 - INFO - DefaultMessageListenerContainer - Successfully refreshed JMS Connection

One thing to notice is that the first slash "/" for the destination disappears, but if I remove the "queue:" from the URI the same error occurs, but the destination becomes '/path/to/queue:queue'.

I have searched for this error and find a question here on stackoverflow: Does anyone know exactly what javax.jms.InvalidDestinationException: Not allowed to create destination means?

Just to be sure that the configuration is correct, I have created the following class (copying the exact same settings from Spring XML to the class):

import java.util.Properties;

import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import java.io.*;

public class Test {

public static void main(String[] args) throws Exception {
    Properties properties = new Properties();

    properties.put("java.naming.provider.url", "...LDAP Server URL...");
    properties.put("java.naming.factory.initial", "...");
    properties.put("java.naming.referral", "follow");
    properties.put("java.naming.security.principal", "uid=...,ou=...,dc=...,dc=...");
    properties.put("java.naming.security.credentials", "...");
    properties.put("JmsConnectionFactory", "/path/to/queueConnectionFactory:qcf");
    properties.put("JmsDestinationName", "/path/to/queue:queue");
    properties.put("JmsUserName", "...");
    properties.put("JmsPassword", "...");

    Context context = null;
    QueueConnection connection = null;
    QueueSession session = null;
    QueueReceiver receiver = null;

    context = new InitialContext(properties);

    Queue queue = (Queue) context.lookup((String) properties.get("JmsDestinationName"));
    QueueConnectionFactory queueConnectionFactory = (QueueConnectionFactory) context.lookup((String) properties.get("JmsConnectionFactory"));

    connection = queueConnectionFactory.createQueueConnection((String) properties.getProperty("JmsUserName"), 
                                                              properties.getProperty("JmsPassword"));

    session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

    connection.start();

    receiver = session.createReceiver(queue);

    while (true) {
        TextMessage message = (TextMessage) receiver.receive();

        System.out.println("Received: " + message.getText());

        BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in));
        String s = bufferRead.readLine();

        if ("b".equals(s)) {
            break;
        }
    }

    connection.close();
}

}

With this class I am able to read the messages that are in the queue.

Have anyone experienced this problem before?

Please let me know if you guys need more information.

Thank you.

È stato utile?

Soluzione 2

Thank you for the help, but the solution was really simple.

I have downloaded the source codes to debug and ended up in a Spring class named DefaultMessageListenerContainer, the exception was thrown when the createConsumer method was called in the session.

This led me to a topic in Spring forum http://forum.spring.io/forum/spring-projects/integration/jms/129634-dbus-jms-message-listener-javax-jms-jmssecurityexception-not-permitted

Basically it says that in order to prevent this error JNDI should be used to get the destination instead of session.createQueue().

Then I searched for a way to use a JNDI name in the endpoint URI and found http://camel.465427.n5.nabble.com/JMS-queue-JNDI-instead-of-physical-name-td494620.html

Which basically tells that a destination resolver is needed. Adding it resolved the error.

<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
    <property name="environment">
        <props>
            <prop...</prop>
        </props>
    </property>
</bean>

<bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiTemplate" ref="jndiTemplate"/>
    <property name="jndiName" value="..."/>
</bean>

<bean id="jmsDestinationResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver">
    <property name="jndiTemplate" ref="jndiTemplate" />
    <property name="cache" value="true" />
</bean>

<bean id="testjms" class="org.apache.camel.component.jms.JmsComponent"> 
    <property name="connectionFactory" ref="jmsQueueConnectionFactory"/> 
    <property name="destinationResolver" ref="jmsDestinationResolver" />
</bean>

<camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring">
    <route id="testRoute">
        <from uri="testjms:jmsJNDIName?connectionFactory=jmsQueueConnectionFactory&amp;username=...&amp;password=..." />
        <to uri="file:C:\inbox" />
    </route>
</camelContext>

tl;dr - Use JNDI to get the queue and specify a destination resolver.

Altri suggerimenti

Sounds like you need to have some Tibco Admin guy to create that queue on the Tibco message broker first.

The name of the createQueue method is misleading. It's not intended to actually create the queue itself, but merely a Queue object referencing an already existing queue (look at the Javadoc).

JMS doesn't provide any API to create (non-temporary) queues. This is why you have to create the queue by other means (generally some administration tool) and then look it up in JNDI.

But some JMS providers "misuse" this method to auto-create the queue when needed, making it non-portable to a different JMS provider. This was merely documented to be allowed in JMS 2.0

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top