Sending Beans as XML with JmsTemplate

by
Tags:
Category:

Introduction

We often want to send XML via web services. We may already have the schema or annotated JAXB2 classes configured in our application. What if we want to send the same format via JMS? By default Spring JMS is configured to send & receive objects serialized. How can we switch to using JAXB2 (or any other OXM marshaling strategy)? The following example assumes we are going from annotations first instead of from XML Schema.

Quick Overview

  1. Annotate Bean with JAXB2
  2. Configure OXM Converter
  3. Integration Test
  4. Visualize Results
  5. Logging Configuration
  6. Maven Configuration

1. Annotate Bean with JAXB2

  • Use JAXB2 annotations for our bean
package com.gordondickens.jmswithoxm;
import java.math.BigDecimal;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "account")
@XmlAccessorType(XmlAccessType.FIELD)
public class Account {
  @XmlElement(required = true)
  private String name;
  @XmlElement
  private String description;
  @XmlElement
  private BigDecimal balance;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getDescription() {
    return description;
  }
  public void setDescription(String description) {
    this.description = description;
  }
  public BigDecimal getBalance() {
    return balance;
  }
  public void setBalance(BigDecimal balance) {
    this.balance = balance;
  }
  @Override
  public String toString() {
    return "Account [name=" + name + ", description=" + description
        + ", balance=" + balance + "]";
  }
}

2. Configure OXM Converter

  • Define our Marshalers – We see defines JAXB2 as our marshaller for the Account class
  • Register our MarshallingMessageConverter – We register the MarshallingMessageConverter to use the JAXB2 marshaller for both inbound and outbound data
  • Register our Converter – In the JmsTemplate, we register our oxmMessageConverter as the messageConverter. This replaces the default SimpleMessageConverter which will relies on Serialization
  • Notice the ActiveMQ namespace?
























3. Integration Test

  • Populate the Account bean
  • Calls convertAndSend on the JmsTemplate to marshal & send the Account
  • Includes a postProcessor callback to log the XML data
  • Calls receiveAndConvert on the JmsTemplate to get & unmarshal the Account
  • Asserts end state
package com.gordondickens.jmswithoxm;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.math.BigDecimal;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessagePostProcessor;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class JmsWithOxmTest {
  private static final Logger logger = LoggerFactory
    .getLogger(JmsWithOxmTest.class);
  private static final String TEST_DEST = "oxmTestQueue";
  @Autowired
  JmsTemplate jmsTemplate;
  @Test
  public void testSendingMessage() {
    Account account = generateTestMessage();
    jmsTemplate.convertAndSend(TEST_DEST, account,
      new MessagePostProcessor() {
      @Override
      public Message postProcessMessage(Message message)
        throws JMSException {
        if (message instanceof BytesMessage) {
          BytesMessage messageBody = (BytesMessage) message;
          messageBody.reset();
          Long length = messageBody.getBodyLength();
          logger.debug("***** MESSAGE LENGTH is {} bytes",
            length);
          byte[] byteMyMessage = new byte[length.intValue()];
          int red = messageBody.readBytes(byteMyMessage);
          logger.debug(
            "***** SENDING MESSAGE - n" +
            "n{}n",
            new String(byteMyMessage));
        }
        return message;
      }
    });
    Account account2 = (Account) jmsTemplate.receiveAndConvert(TEST_DEST);
    assertNotNull("Account MUST return from JMS", account2);
    assertEquals("Name MUST match", account.getName(), account2.getName());
    assertEquals("Description MUST match", account.getDescription(),
      account2.getDescription());
    assertEquals("Balance MUST match", account.getBalance(),
      account2.getBalance());
  }
  private Account generateTestMessage() {
    Account account = new Account();
    account.setBalance(new BigDecimal(12345.67));
    account.setDescription("A no account varmint");
    account.setName("Waskally Wabbit - Gordon Test June 2011");
    logger.debug("Generated Test Message: " + account.toString());
    return account;
  }
}

4. Visualizing Results

  • Run: mvn clean test
  • See Account XML between the block &
  • Output has been Formatted for clarity
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.gordondickens.jmswithoxm.JmsWithOxmTest
INFO  o.s.o.j.Jaxb2Marshaller - Creating JAXBContext
with classes to be bound [class com.gordondickens.jmswithoxm.Account]
DEBUG c.g.j.JmsWithOxmTest - Generated Test Message:
Account [name=Waskally Wabbit, description=A no account
varmint, balance=12345.670000000000072759576141834259033203125]
DEBUG o.s.j.c.JmsTemplate - Executing callback on JMS Session:
ActiveMQSession {id=ID:Technophiliac-61135-1296856347600-2:1:1,started=false}
DEBUG c.g.j.JmsWithOxmTest - ***** MESSAGE LENGTH is 213 bytes
DEBUG c.g.j.JmsWithOxmTest - ***** SENDING MESSAGE -



Waskally Wabbit
A no account varmint
12345.670000000000072759576141834259033203125


DEBUG o.s.j.c.JmsTemplate - Sending created message:
ActiveMQBytesMessage {commandId = 0, responseRequired = false, messageId = null, originalDestination = null, originalTransactionId = null, producerId = null, destination = null, transactionId = null, expiration = 0, timestamp = 0, arrival = 0, brokerInTime = 0, brokerOutTime = 0, correlationId = null, replyTo = null, persistent = false, type = null, priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = org.apache.activemq.util.ByteSequence@b364dcb, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 0, properties = null, readOnlyProperties = false, readOnlyBody = true, droppable = false} ActiveMQBytesMessage{ bytesOut = null, dataOut = null, dataIn = java.io.DataInputStream@1a2d502d }
DEBUG o.s.j.c.JmsTemplate - Executing callback on JMS Session:
ActiveMQSession {id=ID:Technophiliac-61135-1296856347600-2:2:1,started=true}
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.276 sec

5. Logging Configuration



  
    true
  
  
    
      %-5level|%logger{0}|%msg%n
    
  
  
  
  
  
    
  

6. Maven Configuration

  • Using Logback to support Log4J, SLF4J, Apache (JCL) & Java Util Logging
  • Included IDE builders for STS/Eclipse & IntelliJ IDEA


  4.0.0
  com.gordondickens.jmswithoxm
  spring-jms-oxm
  1.1.0
  jar
  JMS to OXM Spring
  http://gordondickens.com
  Sample JMS with OXM Message Conversion
  
    
      gordon.dickens
      Gordon Dickens
      gordondickens@gmail.com
      
        Author
      
      http://www.gordondickens.com
    
  
  
    3.1.0.RELEASE
    1.6
    4.10
    1.1.1
    1.6.4
    5.5.1
    1.0.0
    3.9
    2.2.4
    3.1
    3.0.1
    2.4.1
    2.3.2
    2.4
    2.8
    2.2
    2.5
    2.1.2
    2.11
    UTF-8
    false
  
  
    
      org.springframework
      spring-jms
      ${spring.version}
    
    
      org.springframework
      spring-oxm
    
    
      org.springframework
      spring-test
      ${spring.version}
      test
    
    
      junit
      junit
      ${junit.version}
      test
    
    
      org.slf4j
      jcl-over-slf4j
      ${slf4j.version}
    
    
      org.slf4j
      jul-to-slf4j
      ${slf4j.version}
    
    
      ch.qos.logback
      logback-classic
      ${logback.version}
    
    
      org.apache.geronimo.specs
      geronimo-jms_1.1_spec
      ${jms.version}
    
    
      org.apache.activemq
      activemq-core
    
    
      org.apache.xbean
      xbean-spring
    
  
  
    
      
        junit
        junit
        ${junit.version}
        test
      
      
        ch.qos.logback
        logback-core
        ${logback.version}
      
      
        org.apache.commons
        commons-lang3
        ${commons.lang.version}
      
      
        commons-net
        commons-net
        ${commons.net.version}
      
      
        org.apache.activemq
        activemq-core
        ${activemq.version}
        
          
            org.springframework
            spring-context
          
          
            commons-logging
            commons-logging
          
          
            commons-logging
            commons-logging-api
          
        
      
      
        org.springframework
        spring-core
        ${spring.version}
        
          
            commons-logging
            commons-logging
          
        
      
      
        org.springframework
        spring-oxm
        ${spring.version}
        
          
            commons-lang
            commons-lang
          
        
      
      
        javax.xml.bind
        jaxb-api
        ${jaxb.version}
      
      
        org.apache.xbean
        xbean-spring
        ${xbean.version}
        
          
            commons-logging
            commons-logging
          
        
      
    
  
  
    
      
        org.apache.maven.plugins
        maven-surefire-plugin
        ${maven.surefire.plugin}
      
      
        org.apache.maven.plugins
        maven-eclipse-plugin
        ${maven.eclipse.plugin}
        
          true
          2.0
          
            
              org.springframework.ide.eclipse.core.springbuilder
              org.maven.ide.eclipse.maven2Builder
            
          
          
            org.springframework.ide.eclipse.core.springnature
            org.maven.ide.eclipse.maven2Nature
          
        
      
      
        org.apache.maven.plugins
        maven-compiler-plugin
        ${maven.compiler.plugin}
        
          ${java.version}
          ${java.version}
        
      
      
        org.apache.maven.plugins
        maven-source-plugin
        ${maven.source.plugin}
      
      
        org.apache.maven.plugins
        maven-clean-plugin
        ${maven.clean.plugin}
        
          
            
              ${project.basedir}
              
                activemq-data
                amqstore
                surefire*
              
              false
            
          
        
      
      
        org.apache.maven.plugins
        maven-idea-plugin
        ${maven.idea.plugin}
        
          true
        
      
      
        org.apache.maven.plugins
        maven-resources-plugin
        ${maven.resources.plugin}
      
      
        org.apache.maven.plugins
        maven-dependency-plugin
        ${maven.dependency.plugin}
        
          
            junit
            junit
            ${junit.version}
          
        
      
    
  
  
    
      fast
      
        true
      
    
  

6. Getting the code

Conclusion

Using Spring, it is very easy to configure projects to send/receive data from XML formatted beans. This simplification allows us to focus on the message payload using Spring JMS. If our solution was to serialize the data, the default JMS message conversion would suffice. This implementation focussed on annotated JAXB2 beans without an XML schema. Many projects have existing XML schema, and with Spring JMS this is not difficult to configure. The example above focussed on JAXB2 as our marshaling strategy, we could have easily chosen other strategies such as JiBX, XMLBeans, XStream, Castor with relative ease.

Further Reading