Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to migrate Spring Boot JMS from ActiveMQ to Oracle Advanced Queueing

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!

like image 525
dhmc Avatar asked Apr 17 '17 19:04

dhmc


2 Answers

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 ;-)

like image 97
tm1701 Avatar answered Oct 15 '22 03:10

tm1701


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>
like image 33
Karthik Prasad Avatar answered Oct 15 '22 03:10

Karthik Prasad