I have created a Spring Boot project using initializer and I am trying to create my first message but I have no idea where to start. I am familiar with the same process using JEE so I guess I need to create a factory, a sender and a consumer.
Can someone help me out?
The best place to start is the projects getting started guide
Your approach is correct in general terms but he is what the skeleton looks like.
First spring-boot gives you a perfect configuration file structure and if you are using a smart ide like Netbeans then by adding the spring-boot plugin will give you autocomplete in the properties file too. Since Spring acts a bit differently with each broker, in my examples I will use ActiveMQ
By just having ActiveMQ on our build path, Spring Boot will automatically set up a ActiveMQ broker. We need to set a couple properties to make it an in memory broker, without connection pooling. We can do this by setting two properties for Spring Boot.
spring.activemq.in-memory=true
spring.activemq.pooled=false
jms.bookmgrqueue.name=book-mgr-queue #queue name
Similar configurations can be done for other brokers as well.
First you start with the setup of the Spring application. You should place the @EnableJms
annotation to enable Jms support and then setup a new queue.
Example
@EnableJms
@Configuration
public class JmsConfiguration {
@Autowired
private BeanFactory springContextBeanFactory;
@Bean
public DefaultJmsListenerContainerFactory containerFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setDestinationResolver(new BeanFactoryDestinationResolver(springContextBeanFactory));
factory.setConcurrency("3-10");
return factory;
}
@Bean
public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) throws JMSException {
return new JmsTemplate(connectionFactory);
}
}
Listening to queue messages
The listener component (BookMgrQueueListener.java) is using Spring’s @JmsListener
annotation with selectors to read the messages with a given Operation header.
@Component
public class BookMgrQueueListener implements Loggable{
private final BookService bookService;
@Autowired
public BookMgrQueueListener(BookService bookService) {
this.bookService = bookService;
}
@JmsListener(containerFactory = "containerFactory",
destination = "bookMgrQueueDestination",
selector = "Operation = 'Create'")
public void processCreateBookMessage(BookDTO book) throws JMSException{
bookService.createNew(book);
}
@JmsListener(containerFactory = "containerFactory",
destination = "bookMgrQueueDestination",
selector = "Operation = 'Update'")
public void processUpdateBookMessage(BookDTO book) throws JMSException{
bookService.update(book.getIsbn(), book);
}
@JmsListener(containerFactory = "containerFactory",
destination = "bookMgrQueueDestination",
selector = "Operation = 'Delete'")
public void processDeleteBookMessage(BookDTO book) throws JMSException{
bookService.delete(book.getIsbn());
}
}
Active MQ for test
To test the configuration we are setting up activeMq broker in a new configuration file, ActiveMqConfiguration.java.
@Configuration
public class ActiveMqConfiguration {
public static final String ADDRESS = "vm://localhost";
private BrokerService broker;
@Bean(name="bookMgrQueueDestination")
public Destination bookMgrQueueDestination(@Value("${jms.bookmgrqueue.name}") String bookMgrQueueName)
throws JMSException {
return new ActiveMQQueue(bookMgrQueueName);
}
@PostConstruct
public void startActiveMQ() throws Exception {
broker = new BrokerService();
// configure the broker
broker.setBrokerName("activemq-broker");
broker.setDataDirectory("target");
broker.addConnector(ADDRESS);
broker.setUseJmx(false);
broker.setUseShutdownHook(false);
broker.start();
}
@PreDestroy
public void stopActiveMQ() throws Exception {
broker.stop();
}
@Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory(ADDRESS + "?broker.persistent=false");
}
}
We are setting up a full application context in the testcase but we are replacing the BookService reference in the listener to a MockedBookService which we will use to verify whether the correct calls were executed.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class, loader = SpringApplicationContextLoader.class)
@WebAppConfiguration
public class BookMgrQueueListenerIntegrationTest {
@Autowired(required = false)
private JmsTemplate jmsTemplate;
@Autowired
private BookMgrQueueListener bookMgrQueueListener;
@Autowired(required = false)
@Qualifier("bookMgrQueueDestination")
private Destination bookMgrQueueDestination;
@Mock
private BookService mockBookService;
@Captor
private ArgumentCaptor<BookDTO> bookArgumentCaptor;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(bookMgrQueueListener, "bookService", mockBookService);
}
/* ... tests */
}
Finally we add tests for all operations and verify whether the service layer was called with the correct operations and parameters.
/* ... */
public class BookMgrQueueListenerIntegrationTest {
/* ... */
@Test
public void testSendCreateBookMessage(){
BookDTO book = new BookDTO("isbn", "title", "author");
jmsTemplate.convertAndSend(bookMgrQueueDestination, book, Message -> {
return OperationHeader.CREATE.applyToMessage(Message);
});
// verify
verify(mockBookService).createNew(bookArgumentCaptor.capture());
assertEquals(book.getIsbn(), bookArgumentCaptor.getValue().getIsbn());
assertEquals(book.getTitle(), bookArgumentCaptor.getValue().getTitle());
assertEquals(book.getAuthor(), bookArgumentCaptor.getValue().getAuthor());
}
@Test
public void testSendUpdateBookMessage(){
BookDTO book = new BookDTO("isbn", "title", "author");
jmsTemplate.convertAndSend(bookMgrQueueDestination, book, Message -> {
return OperationHeader.UPDATE.applyToMessage(Message);
});
// verify
verify(mockBookService).update(eq(book.getIsbn()), bookArgumentCaptor.capture());
assertEquals(book.getIsbn(), bookArgumentCaptor.getValue().getIsbn());
assertEquals(book.getTitle(),bookArgumentCaptor.getValue().getTitle());
assertEquals(book.getAuthor(),bookArgumentCaptor.getValue().getAuthor());
}
@Test
public void testSendDeleteBookMessage(){
BookDTO book = new BookDTO("isbn", "title", "author");
jmsTemplate.convertAndSend(bookMgrQueueDestination, book, Message -> {
return OperationHeader.DELETE.applyToMessage(Message);
});
// verify
verify(mockBookService).delete(book.getIsbn());
}
And we are good to go!