How to migrate Spring Boot JMS from ActiveMQ to Oracle Advanced Queueing
Asked Answered
D

2

9

I'm studying the Spring Boot and JMS example and yes, I'm rather new on this

Since we work with Oracle, I would like to migrate the Spring Boot & JMS example from ActiveMQ to Oracle Advanced Queueing. However I really find very little information on that.

As far as I see I need to replace the code below for the Oracle version, yet I did not manage to found out how.

@Bean
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
                                                DefaultJmsListenerContainerFactoryConfigurer configurer) {
    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
    // This provides all boot's default to this factory, including the message converter
    configurer.configure(factory, connectionFactory);
    // You could still override some of Boot's default if necessary.
    return factory;

}

The origin code can be found at Github

Help would be greatly appreciated!

Deckhouse answered 17/4, 2017 at 19:46 Comment(4)
IMHO is it intentional. If your code runs on WebLogic (which is also Oracle product) then the setup is quite easy (see danielveselka.blogspot.cz/2009/07/…). I'm afraid other application servers do not offer JMS to AQ connectors(resource adapters).Regress
No, I do not want to connect to WebLogic, but directly to Oracle Advanced Queueing (AQ). In my program without Spring Boot I use: ` qconFactory = AQjmsFactory.getQueueConnectionFactory(hostName, serviceName, port, "thin"); qcon = qconFactory.createQueueConnection(userName, password); qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); queueTable = ((AQjmsSession) qsession).getQueueTable(userName, queueTableName); queue = ((AQjmsSession) qsession).getQueue(userName, queueName); `Deckhouse
What as was trying explain was. Oracle AQ can act as standard Java EE JMS provider. Which might make your transition from ActiveMQ much easier. (In fact there would be no changes in the source code). But this might work only under some circumstances.Regress
I'm also interested in this. Not the migration so much, but Oracle AQ in Spring.Limen
F
4

The config below will solve your questions.

1 - Create the configuration. For this answer, I have put all config files compact in the Application file. You can put them in seperate classes thus seperating concerns.

@SpringBootApplication
@EnableJms
public class Application {
    private static Random rand = new Random();

    @Bean
    DataSource dataSource() throws SQLException {
        OracleDataSource dataSource = new OracleDataSource();
        dataSource.setUser("yourusername");
        dataSource.setPassword("yourpassword");
        dataSource.setURL("jdbc:oracle:thin:@yourserver:1521:xe");
        dataSource.setImplicitCachingEnabled(true);
        dataSource.setFastConnectionFailoverEnabled(true);
        return dataSource;
    }    

    @Bean
    public QueueConnectionFactory connectionFactory() throws Exception {
        return AQjmsFactory.getQueueConnectionFactory(dataSource());
    }

    @Bean
    public JmsTemplate jmsTemplate() throws Exception {
        JmsTemplate jmsTemplate = new JmsTemplate();
        jmsTemplate.setConnectionFactory(connectionFactory());
        jmsTemplate.setMessageConverter(jacksonJmsMessageConverter());
        return jmsTemplate;
    }

    @Bean
    public JmsListenerContainerFactory<?> myJMSListenerFactory(QueueConnectionFactory connectionFactory,                                                      DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        //  factory.setConcurrency("15-20");
        factory.setMessageConverter(jacksonJmsMessageConverter());
        configurer.configure(factory, connectionFactory);
        return factory;
    }

    @Bean
    public MessageConverter jacksonJmsMessageConverter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setTargetType(MessageType.TEXT);
        converter.setTypeIdPropertyName("_type");
        return converter;
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
        for (int i = 0; i < 10; i++) {
            int waitSecs = rand.nextInt(3);
            jmsTemplate.convertAndSend("YourQueueName", new Email("[email protected]", "Hello " + i, waitSecs));
        }
    }
}

2 - Make your JMS listener

@Component
public class Receiver {
    @JmsListener(destination = "YourQueueName", containerFactory = "myJMSListenerFactory")
    public void receiveEmail(Email email) {
        System.out.println("Received <" + email + ">");
    }
}

3 - Maven and Oracle

You can add the oracle6 or oracle7 jars seperately to your lib path, as shown in this post.

The rest of the Mavan file is pretty standard.

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

4 - Business objects. Objects like Email are your business domain POJO's. I won't put them here.

@Testing, this works like charm ;-)

Frivolity answered 23/2, 2018 at 14:0 Comment(0)
O
2

I don't think you need to change the myFactory method as such, instead you need to create connectionFactory, which connects to the oracle queue. I had similar configuration, in dev I used artemis to run my JUNIT and in prod I used oracle queue. Below is the class I defined to create connectionFactory.

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.naming.Context;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.jndi.JndiTemplate;


/**
 * @author Karthik Prasad
 * @since 1.0.0.0
 *        <p>
 *        Configuration file for weblogic JMS connection
 */
@Configuration
@EnableJms
@ConfigurationProperties(prefix = "spring.wls.jms")
@ConditionalOnProperty(prefix = "spring.wls.jms", name = "url")
public class WLSJmsConfiguration {

    /**
     * SJ4J Log instance
     */
    private static final Logger LOG = LoggerFactory.getLogger(WLSJmsConfiguration.class);

    /**
     * provider url
     */
    private String url;
    /**
     * username of weblogic server using which JNDI connection will be
     * established
     */
    private String username;
    /**
     * password of weblogic server using which JNDI connection will be
     * established
     */
    private String password;
    /**
     * JMS Connection factory name configured in weblogic server
     */
    private String connectionFactoryName;

    /**
     * Name of destination queue
     */
    private String targetQueue;

    /**
     * The Response Queue
     */
    private String replyQueue;


    /**
     * URL to access weblogic Connectionfactory, property is set from properties
     * file
     * 
     * @see ConfigurationProperties
     * @param password
     *            weblogic url to JNDI
     */
    public void setUrl(final String url) {
        this.url = url;
    }

    /**
     * username to access weblogic queue, property is set from properties file
     * 
     * @see ConfigurationProperties
     * @param username
     *            weblogic username to access queue
     */
    public void setUsername(final String username) {
        this.username = username;
    }

    /**
     * Password to access weblogic queue, property is set from properties file
     * 
     * @see ConfigurationProperties
     * @param password
     *            weblogic password to access queue
     */
    public void setPassword(final String password) {
        this.password = password;
    }

    /**
     * Setter of connection factory name, property is set from properties file
     * 
     * @see ConfigurationProperties
     * @param connectionFactoryName
     *            ConnectionFactory from properties file
     */
    public void setConnectionFactoryName(final String connectionFactoryName) {
        this.connectionFactoryName = connectionFactoryName;
    }

    /**
     * Setter for {@link #targetQueue}
     * 
     * @param targetQueue
     *            the targetQueue to set
     */
    public void setTargetQueue(final String targetQueue) {
        this.targetQueue = targetQueue;
    }

    /**
     * @param replyQueue
     *            the replyQueue to set
     */
    public void setReplyQueue(final String replyQueue) {
        this.replyQueue = replyQueue;
    }


    /**
     * Get JNDI properties from properties file
     * 
     * @return list of Weblogic jndi properties
     */
    private Properties getJNDiProperties() {

        final Properties jndiProps = new Properties();
        LOG.debug("Initializing JndiTemplate");
        LOG.debug("Url is {}", url);
        jndiProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
        jndiProps.setProperty(Context.PROVIDER_URL, url);
        if (username != null && !username.isEmpty()) {
            jndiProps.setProperty(Context.SECURITY_PRINCIPAL, username);
        }
        if (password != null && !password.isEmpty()) {
            jndiProps.setProperty(Context.SECURITY_CREDENTIALS, password);
        }
        return jndiProps;

    }

    /**
     * Create JndiTemplate for target weblogic server from provided JNDI
     * properties
     * 
     * @return Bean of Jndi Template
     */
    @Bean
    public JndiTemplate jndiTemplate() {
        final JndiTemplate jndiTemplate = new JndiTemplate();
        jndiTemplate.setEnvironment(getJNDiProperties());
        return jndiTemplate;
    }

    /**
     * Creates instance of Jndi Object Factory bean from Jndi Template
     * 
     * @param jndiTemplate
     *            Jndi Template for weblogic server
     * @return Bean of JndiObject Factory
     */
    @Bean(name = "jmsJndiConnectionFactory")
    public JndiObjectFactoryBean jndiObjectFactoryBean(final JndiTemplate jndiTemplate) {

        final JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        LOG.debug("Creating Weblogic JMS connection factory");
        jndiObjectFactoryBean.setJndiTemplate(jndiTemplate);
        // connectionFactory name.
        LOG.debug("ConnectoinFactory Name is {}", connectionFactoryName);
        jndiObjectFactoryBean.setJndiName(connectionFactoryName);
        return jndiObjectFactoryBean;

    }

    /**
     * Create Jms Connection factory from Jndi Objectfactory
     * 
     * @param jndiObjectFactoryBean
     *            Jndi Object factory bean
     * @return Returns Jms Connection factory Bean
     */
    @Bean(name = "jmsWlsConnectionFactory")
    public ConnectionFactory jmsConnectionFactory(final JndiObjectFactoryBean jndiObjectFactoryBean) {
        final ConnectionFactory connectionFactory = (ConnectionFactory) jndiObjectFactoryBean.getObject();
        LOG.debug("ConnectoinFactory is null? {}", connectionFactory == null);
        return connectionFactory;
    }

    /**
     * Wrap Weblogic Connection Factory around caching factory
     * 
     * @return
     */
    @Bean(name = "jmsConnectionFactory")
    @Primary
    public ConnectionFactory connectionFactoryProxy() {
        final CachingConnectionFactory jmsConnectionFactory = new CachingConnectionFactory(
                (ConnectionFactory) appContext.getBean("jmsWlsConnectionFactory"));
        jmsConnectionFactory.setCacheProducers(true);
        jmsConnectionFactory.setSessionCacheSize(20);
        return jmsConnectionFactory;
    }

    /**
     * The instance of Target Queue retrieved from JNDI, this bean is created in
     * dev profile, where one want to run the project in standalone mode but
     * want to connect to Weblogic Server
     * 
     * @return Bean of target queue instance
     */
    @Bean
    public Destination jmsQueueName() {

        final JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiTemplate(jndiTemplate());
        jndiObjectFactoryBean.setJndiName(targetQueue); // queue name
        return (Destination) jndiObjectFactoryBean.getObject();
    }

    /**
     * Create DestinationResolver to resolve QueueName
     * 
     * @return Instance of JNDI Destination Resolver
     */
    private DestinationResolver destinationResolver() {
        final JMSDestinationResolver destinationResolver = new JMSDestinationResolver();
        final JndiHelper jndiHelper = new JndiHelper(getJNDiProperties());
        destinationResolver.setJndiTemplate(jndiHelper);
        return destinationResolver;
    }

}

The application.properties.

spring.wls.jms.url=t3://server01:8001,server02:8003
spring.wls.jms.username=weblogic
spring.wls.jms.password=password
spring.wls.jms.connectionFactoryName=connectionFactory Name
spring.wls.jms.targetQueue=queue_name
spring.wls.jms.replyQueue=queue_name

And you need to add wlthint3client to your classpath. I got the jar from <weblogic_home>\wlserver\server\lib and created maven dependency from jar and pushed to my local repo and added the jar as dependency.

    <dependency>
        <groupId>com.oracle.weblogic</groupId>
        <artifactId>wlthint3client</artifactId>
        <version>12.2.1</version>
        <scope>provided</scope> <!-- comment out this if you are deploying on tomcat or running the application standalone -->
    </dependency>
Orlena answered 20/4, 2017 at 20:20 Comment(1)
Thanks for your answer. Great for everyone using WebLogic. However how can I connect to Oracle Advanced Queueing (AQ) WITHOUT THE NEED OF WEBLOGIC. So what is the Spring Boot Oracle AQ equivalent of the ActiveMQ code in the Getting Started with JMS example, see [Spring Boot Getting started JMS][1] Or the the [spring-boot-sample-activemq example on Github][2] [1]: spring.io/guides/gs/messaging-jms [2]: github.com/spring-projects/spring-boot/tree/master/…Deckhouse

© 2022 - 2024 — McMap. All rights reserved.