Spring JMS, Message Automatic Conversion, JMS Template

In one of my projects I was supposed to create a message router that like all routers was supposed to take the JMS messages from one topic and put it into another one. The message itself was a JMS text message that in fact contained an XML message. What is more after having received it I was supposed to enrich the message with some additional data.

We were not allowed to use neither Spring nor JAXB nor any other useful library so I decided to check how easy it would be to do it using them. Initially I wanted to use only Spring and JAXB but in the next post I will try to repeat the same scenario by using Apache Camel (that's why you will find the word "camel" in the package name). The JMS communication was present thanks to the ActiveMQ messaging server.

Anyway coming back to the code.

I used maven to resolve dependencies and these are the dependencies that were mandatory i n terms of JMS and JAXB and message conversion:

pom.xml

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-jms</artifactId>
 <version>3.1.1.RELEASE</version>
</dependency>
<dependency>
 <groupId>com.sun.xml.bind</groupId>
 <artifactId>jaxb-impl</artifactId>
 <version>2.2.6</version>
</dependency>
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-oxm</artifactId>
 <version>3.1.1.RELEASE</version>
</dependency>




This is how I divided the project (the camel part of the package will make more sense in the next article).


In order to have my message converted to objects via JAXB I needed a schema:

Player.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

 <xsd:element name="PlayerDetails">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element name="Name" type="xsd:string" />
    <xsd:element name="Surname" type="xsd:string" />
    <xsd:element name="Position" type="PositionType" />
    <xsd:element name="Age" type="xsd:int" />
    <xsd:element name="TeamName" type="xsd:string" />
   </xsd:sequence>
  </xsd:complexType>
 </xsd:element>


 <xsd:simpleType name="PositionType">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="GK" />
   <xsd:enumeration value="DEF" />
   <xsd:enumeration value="MID" />
   <xsd:enumeration value="ATT" />
  </xsd:restriction>
 </xsd:simpleType>

</xsd:schema>
I had to download JAXB binaries and executed the following command to have my objects created:
./xjc.sh -p pl.grzejszczak.marcin.camel.jaxb.generated ~/PATH/TO/THE/SCHEMA/FILE/Player.xsd

An example of the outcome of this command is the:

PlayerDetails.java
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.6
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2012.11.05 at 09:23:22 PM CET
//


package pl.grzejszczak.marcin.camel.jaxb.generated;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;


/**
 * Java class for anonymous complex type.
 *
 *

The following schema fragment specifies the expected content contained within this class.
 *
 *

<pre> * <complexType>
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element name="Name" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *         <element name="Surname" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *         <element name="Position" type="{}PositionType"/>
 *         <element name="Age" type="{http://www.w3.org/2001/XMLSchema}int"/>
 *         <element name="TeamName" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *       </sequence>
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
*
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "name",
    "surname",
    "position",
    "age",
    "teamName"
})
@XmlRootElement(name = "PlayerDetails")
public class PlayerDetails {

    @XmlElement(name = "Name", required = true)
    protected String name;
    @XmlElement(name = "Surname", required = true)
    protected String surname;
    @XmlElement(name = "Position", required = true)
    protected PositionType position;
    @XmlElement(name = "Age")
    protected int age;
    @XmlElement(name = "TeamName", required = true)
    protected String teamName;

    /**
     * Gets the value of the name property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getName() {
        return name;
    }

    /**
     * Sets the value of the name property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setName(String value) {
        this.name = value;
    }

    /**
     * Gets the value of the surname property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getSurname() {
        return surname;
    }

    /**
     * Sets the value of the surname property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setSurname(String value) {
        this.surname = value;
    }

    /**
     * Gets the value of the position property.
     *
     * @return
     *     possible object is
     *     {@link PositionType }
     *    
     */
    public PositionType getPosition() {
        return position;
    }

    /**
     * Sets the value of the position property.
     *
     * @param value
     *     allowed object is
     *     {@link PositionType }
     *    
     */
    public void setPosition(PositionType value) {
        this.position = value;
    }

    /**
     * Gets the value of the age property.
     *
     */
    public int getAge() {
        return age;
    }

    /**
     * Sets the value of the age property.
     *
     */
    public void setAge(int value) {
        this.age = value;
    }

    /**
     * Gets the value of the teamName property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getTeamName() {
        return teamName;
    }

    /**
     * Sets the value of the teamName property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setTeamName(String value) {
        this.teamName = value;
    }

}
The @XmlRootElement(name = "PlayerDetails") means that this class will output a Root node in the XML file. The @XmlAccessorType(XmlAccessType.FIELD) as the JavaDoc says means that "Every non static, non transient field in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient." In other words, if you have a field annotated by the XmlTransient annotation it won't get serialized. Then we have the @XmlType(name = "", propOrder = { "name", "surname", "position", "age", "teamName" })which as JavaDoc sates "Maps a class or an enum type to a XML Schema type" . In other words our class is mapped to the PlayerDetails element in the schema. Finally we have the @XmlElement(name = "Name", required = true) annotation which is a mapping of the XML node (element) to a field in the class.

This is my message to be sent, received, enriched and routed:

RobertLewandowski.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PlayerDetails>
    <Name>Robert</Name>
    <Surname>Lewandowski</Surname>
    <Position>ATT</Position>
</PlayerDetails>

Now off to my JMS configuration - I have configured the Queues of origin and destination

jms.properties
jms.origin=Initial.Queue
jms.destination=Routed.Queue

This is my Spring configuration (I added comments inside the config that explain the origin of those components):

jmsApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:jms="http://www.springframework.org/schema/jms" xmlns:oxm="http://www.springframework.org/schema/oxm"
 xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/jmshttp://www.springframework.org/schema/jms/spring-jms-3.0.xsdhttp://www.springframework.org/schema/oxmhttp://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd">

 <!-- Spring configuration based on annotations -->
 <context:annotation-config />
 <!-- Show Spring where to search for the beans (in which packages) -->
 <context:component-scan base-package="pl.grzejszczak.marcin.camel" />

 <!-- Show Spring where to search for the properties files -->
 <context:property-placeholder location="classpath:/camel/jms.properties" />

 <!-- The ActiveMQ connection factory with specification of the server URL -->
 <bean id="activeMQConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
  <property name="brokerURL" value="tcp://localhost:61616" />
 </bean>

 <!-- Spring's jms connection factory -->
 <bean id="cachingConnectionFactory"
  class="org.springframework.jms.connection.CachingConnectionFactory">
  <property name="targetConnectionFactory" ref="activeMQConnectionFactory" />
  <property name="sessionCacheSize" value="10" />
 </bean>

 <!-- The name of the queue from which we will take the messages -->
 <bean id="origin" class="org.apache.activemq.command.ActiveMQQueue">
  <constructor-arg value="${jms.origin}" />
 </bean>
 <!-- The name of the queue to which we will route the messages -->
 <bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
  <constructor-arg value="${jms.destination}" />
 </bean>

 <!-- Configuration of the JmsTemplate together with the connection factory and the message converter -->
 <bean id="producerTemplate" class="org.springframework.jms.core.JmsTemplate">
  <property name="connectionFactory" ref="cachingConnectionFactory" />
  <property name="messageConverter" ref="oxmMessageConverter" />
 </bean>

 <!-- Custom message sender sending messages to the initial queue -->
 <bean id="originPlayerSender" class="pl.grzejszczak.marcin.camel.manual.jms.PlayerDetailsSenderImpl">
  <property name="destination" ref="origin" />
 </bean>
 <!-- Custom message sender sending messages to the destination queue -->
 <bean id="destinationPlayerSender" class="pl.grzejszczak.marcin.camel.manual.jms.PlayerDetailsSenderImpl">
  <property name="destination" ref="destination" />
 </bean>

 <!-- Custom message listener - listens to the initial queue  -->
 <bean id="originListenerImpl" class="pl.grzejszczak.marcin.camel.manual.jms.ListenerImpl"/>
 <!-- Custom message listener - listens to the destination queue  -->
 <bean id="destinationListenerImpl" class="pl.grzejszczak.marcin.camel.manual.jms.FinalListenerImpl"/>

 <!-- Spring's jms message listener container - specified the connection factory, the queue to be listened to and the component that listens to the queue -->
 <bean id="jmsOriginContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
  <property name="connectionFactory" ref="cachingConnectionFactory" />
  <property name="destination" ref="origin" />
  <property name="messageListener" ref="originListenerImpl" />
 </bean>

 <!-- Spring's jms message listener container - specified the connection factory, the queue to be listened to and the component that listens to the queue -->
 <bean id="jmsDestinationContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
  <property name="connectionFactory" ref="cachingConnectionFactory" />
  <property name="destination" ref="destination" />
  <property name="messageListener" ref="destinationListenerImpl" />
 </bean>

 <!-- Message converter - automatically marshalls and unmarshalls messages using the provided marshaller / unmarshaller-->
 <bean id="oxmMessageConverter" class="org.springframework.jms.support.converter.MarshallingMessageConverter">
          <property name="marshaller" ref="marshaller" />
          <property name="unmarshaller" ref="marshaller" />
     </bean>

 <!-- Spring's JAXB implementation of marshaller - provided a class the JAXB generated class -->
     <oxm:jaxb2-marshaller id="marshaller">
          <oxm:class-to-be-bound name="pl.grzejszczak.marcin.camel.jaxb.generated.PlayerDetails" />
     </oxm:jaxb2-marshaller>

</beans>
Now let's take a look at the Java code - let's start with the class that has the main function

ActiveMQRouter.java
package pl.grzejszczak.marcin.camel.manual;

import java.io.File;
import java.util.Scanner;

import javax.jms.JMSException;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import pl.grzejszczak.marcin.camel.jaxb.PlayerDetailsConverter;
import pl.grzejszczak.marcin.camel.jaxb.generated.PlayerDetails;
import pl.grzejszczak.marcin.camel.manual.jms.Sender;

public class ActiveMQRouter {

 /**
  * @param args
  * @throws JMSException
  */
 public static void main(String[] args) throws Exception {
  ApplicationContext context = new ClassPathXmlApplicationContext("/camel/jmsApplicationContext.xml");
  @SuppressWarnings("unchecked")
  Sender<PlayerDetails> sender = (Sender<PlayerDetails>) context.getBean("originPlayerSender");

  Resource resource = new ClassPathResource("/camel/RobertLewandowski.xml");

  Scanner scanner = new Scanner(new File(resource.getURI())).useDelimiter("\\Z");
  String contents = scanner.next();

  PlayerDetailsConverter converter = context.getBean(PlayerDetailsConverter.class);

  sender.sendMessage(converter.unmarshal(contents));
 }
}

What we can see here is that we initialize the Spring context from the classpath and retrieve the bean named originPlayerSender. This component is used for sending a message to the initial queue. In order to have a message to send we are retrieving a file RobertLewandowski.xml from the classpath and read it to a String variable through the Scanner class. Next we use our custom PlayerDetailsConverter class to unmarshall the String contents into a PlayerDetails object, which in effect is sent by the originPlayerSender to the origin queue.

Now let's take a look at the sender logic:

PlayerDetailsSenderImpl.java
package pl.grzejszczak.marcin.camel.manual.jms;

import javax.jms.Destination;
import javax.jms.JMSException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;

import pl.grzejszczak.marcin.camel.jaxb.generated.PlayerDetails;

@Component
public class PlayerDetailsSenderImpl implements Sender<PlayerDetails> {

 private static final Logger LOGGER = LoggerFactory.getLogger(PlayerDetailsSenderImpl.class);

 private Destination destination;

 @Autowired
 private JmsTemplate jmsTemplate;

 @Override
 public void sendMessage(final PlayerDetails object) throws JMSException {
  LOGGER.debug("Sending [{}] to topic [{}]", new Object[] { object, destination });
  jmsTemplate.convertAndSend(destination, object);
 }

 public Destination getDestination() {
  return destination;
 }

 public void setDestination(Destination destination) {
  this.destination = destination;
 }

}

This class is implementing my Sender interface that provides the sendMessage function. We are using the JmsTemplate object to convert and send the message to the given destination that is injected via Spring.

Ok, now that we've sent the message someone has to retrieve it:

ListenerImpl.java
package pl.grzejszczak.marcin.camel.manual.jms;

import java.util.List;

import javax.jms.BytesMessage;
import javax.jms.Message;
import javax.jms.MessageListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.stereotype.Component;

import pl.grzejszczak.marcin.camel.enricher.Enrichable;
import pl.grzejszczak.marcin.camel.jaxb.Convertable;
import pl.grzejszczak.marcin.camel.jaxb.generated.PlayerDetails;

@Component
public class ListenerImpl implements MessageListener {

 private static final Logger LOG = LoggerFactory.getLogger(ListenerImpl.class);

 @Autowired
 private Convertable<PlayerDetails> playerDetailsConverter;

 @Autowired
 private List<Enrichable<PlayerDetails>> listOfEnrichers;

 @Autowired
 private MessageConverter messageConverter;

 @Autowired
 @Qualifier("destinationPlayerSender")
 private Sender<PlayerDetails> sender;

 @Override
 public void onMessage(Message message) {
  if (!(message instanceof BytesMessage)) {
   LOG.error("Wrong msg!");
   return;
  }

  PlayerDetails playerDetails = null;
  try {
   playerDetails = (PlayerDetails) messageConverter.fromMessage(message);

   LOG.debug("Enriching the input message");
   for (Enrichable<PlayerDetails> enrichable : listOfEnrichers) {
    enrichable.enrich(playerDetails);
   }
   LOG.debug("Enriched text message: [{}]", new Object[] { playerDetailsConverter.marshal(playerDetails) });
   sender.sendMessage(playerDetails);
  } catch (Exception e) {
   LOG.error("Exception occured", e);
  }

 }

}

This class has the list of all the classes implementing the Enrichable interface thanks to which it will provide the enrichment of the message without the necessity of knowing the amount of enrichers in the system. There is also the PlayerDetailsConverter class that helps with marshalling and unmarshalling PlayerDetails. Once the message is enriched it is sent to the destination queue through the bean that implements the Sender interface and has the id of destinationPlayerSender. It is important to remember that what we receive from the queue is a BytesMessage thus that's why we are doing the initial check.

Let's take a look at one of the enrichers (the other one is a setting another field in the PlayerDetails object)

ClubEnricher.java
package pl.grzejszczak.marcin.camel.enricher;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import pl.grzejszczak.marcin.camel.jaxb.generated.PlayerDetails;

@Component("ClubEnricher")
public class ClubEnricher implements Enrichable<PlayerDetails> {

 private static final Logger LOGGER = LoggerFactory.getLogger(ClubEnricher.class);

 @Override
 public void enrich(PlayerDetails inputObject) {
  LOGGER.debug("Enriching player [{}] with club data", new Object[] { inputObject.getSurname() });
  // Simulating accessing DB or some other service
  try {
   Thread.sleep(2000);
  } catch (InterruptedException e) {
   LOGGER.error("Exception while sleeping occured", e);
  }
  inputObject.setTeamName("Borussia Dortmund");
 }

}

As you can see the class is just simulating some access to the DB or any other service and afterwards is setting the team name in the input PlayerDetails object.

Let's now take a look a the conversion mechanism:

PlayerDetailsConverter.java
package pl.grzejszczak.marcin.camel.jaxb;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.apache.activemq.util.ByteArrayInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import pl.grzejszczak.marcin.camel.jaxb.generated.PlayerDetails;

@Component("PlayerDetailsConverter")
public class PlayerDetailsConverter implements Convertable<PlayerDetails> {
 private static final Logger LOGGER = LoggerFactory.getLogger(PlayerDetailsConverter.class);

 private final JAXBContext jaxbContext;
 private final Marshaller jaxbMarshaller;
 private final Unmarshaller jaxbUnmarshaller;

 public PlayerDetailsConverter() throws JAXBException {
  jaxbContext = JAXBContext.newInstance(PlayerDetails.class);
  jaxbMarshaller = jaxbContext.createMarshaller();
  jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
  jaxbUnmarshaller = jaxbContext.createUnmarshaller();
 }

 @Override
 public String marshal(PlayerDetails object) {
  OutputStream stream = new ByteArrayOutputStream();
  try {
   jaxbMarshaller.marshal(object, stream);
  } catch (JAXBException e) {
   LOGGER.error("Exception occured while marshalling", e);
  }
  return stream.toString();
 }

 @Override
 public PlayerDetails unmarshal(String objectAsString) {
  try {
   return (PlayerDetails) jaxbUnmarshaller.unmarshal(new ByteArrayInputStream(objectAsString.getBytes()));
  } catch (JAXBException e) {
   LOGGER.error("Exception occured while marshalling", e);
  }
  return null;
 }

}

In the constructor we are setting some JAXB components - the JAXBContext, JAXB Marshaller and JAXB Unmarshaller that have the necessary marshal and unmarshal methods.

Last but not least is the FinalListenerImpl that is listening to the inbound message from the destination queue and shuts the application.

FinalListenerImpl.java
package pl.grzejszczak.marcin.camel.manual.jms;

import javax.jms.BytesMessage;
import javax.jms.Message;
import javax.jms.MessageListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.stereotype.Component;

import pl.grzejszczak.marcin.camel.jaxb.generated.PlayerDetails;

@Component
public class FinalListenerImpl implements MessageListener {

 private static final Logger LOG = LoggerFactory.getLogger(FinalListenerImpl.class);

 @Autowired
 private MessageConverter messageConverter;

 @Override
 public void onMessage(Message message) {
  if (!(message instanceof BytesMessage)) {
   LOG.error("Wrong msg!");
   return;
  }

  PlayerDetails playerDetails = null;
  try {
   playerDetails = (PlayerDetails) messageConverter.fromMessage(message);

   if (playerDetails.getTeamName() != null) {
    LOG.debug("Message already enriched! Shutting down the system");
   } else {
    LOG.debug("The message should have been enriched but wasn't");
   }

  } catch (Exception e) {
   LOG.error("Exception occured", e);
  } finally {
   System.exit(0);
  }

 }

}

By using the MessageConverter, after having verified if the message is of proper type, we check if the team name has already been filled in - if that is the case we are terminating the application.

And the logs are as follows:
2012-11-05 [main] org.springframework.context.support.ClassPathXmlApplicationContext:495 Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@34fbb7cb: startup date [Mon Nov 05 21:47:00 CET 2012]; root of context hierarchy
2012-11-05 [main] org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315 Loading XML bean definitions from class path resource [camel/jmsApplicationContext.xml]
2012-11-05 [main] org.springframework.beans.factory.config.PropertyPlaceholderConfigurer:177 Loading properties file from class path resource [camel/jms.properties]
2012-11-05 [main] org.springframework.beans.factory.support.DefaultListableBeanFactory:557 Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@3313beb5: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,myRoute,AgeEnricher,ClubEnricher,PlayerDetailsConverter,finalListenerImpl,listenerImpl,playerDetailsSenderImpl,org.springframework.beans.factory.config.PropertyPlaceholderConfigurer#0,activeMQConnectionFactory,cachingConnectionFactory,origin,destination,producerTemplate,originPlayerSender,destinationPlayerSender,originListenerImpl,destinationListenerImpl,jmsOriginContainer,jmsDestinationContainer,oxmMessageConverter,marshaller,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy
2012-11-05 [main] org.springframework.oxm.jaxb.Jaxb2Marshaller:436 Creating JAXBContext with classes to be bound [class pl.grzejszczak.marcin.camel.jaxb.generated.PlayerDetails]
2012-11-05 [main] org.springframework.context.support.DefaultLifecycleProcessor:334 Starting beans in phase 2147483647
2012-11-05 [main] org.springframework.jms.connection.CachingConnectionFactory:291 Established shared JMS Connection: ActiveMQConnection {id=ID:marcin-SR700-38535-1352148424687-1:1,clientId=null,started=false}
2012-11-05 [main] pl.grzejszczak.marcin.camel.manual.jms.PlayerDetailsSenderImpl:26 Sending [pl.grzejszczak.marcin.camel.jaxb.generated.PlayerDetails@6ae2d0b2] to topic [queue://Initial.Queue]
2012-11-05 [jmsOriginContainer-1] pl.grzejszczak.marcin.camel.manual.jms.ListenerImpl:49 Enriching the input message
2012-11-05 [jmsOriginContainer-1] pl.grzejszczak.marcin.camel.enricher.AgeEnricher:17 Enriching player [Lewandowski] with age data
2012-11-05 [jmsOriginContainer-1] pl.grzejszczak.marcin.camel.enricher.ClubEnricher:16 Enriching player [Lewandowski] with club data
2012-11-05 [jmsOriginContainer-1] pl.grzejszczak.marcin.camel.manual.jms.ListenerImpl:53 Enriched text message: [<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PlayerDetails>
    <Name>Robert</Name>
    <Surname>Lewandowski</Surname>
    <Position>ATT</Position>
    <Age>19</Age>
    <TeamName>Borussia Dortmund</TeamName>
</PlayerDetails>
]
2012-11-05 [jmsOriginContainer-1] pl.grzejszczak.marcin.camel.manual.jms.PlayerDetailsSenderImpl:26 Sending [pl.grzejszczak.marcin.camel.jaxb.generated.PlayerDetails@3dca1588] to topic [queue://Routed.Queue]
2012-11-05 [jmsDestinationContainer-1] pl.grzejszczak.marcin.camel.manual.jms.FinalListenerImpl:35 Message already enriched! Shutting down the system

This is how thanks to the Spring JMS module and the JAXB library you can easilly create JMS listeners, senders and message convertors for the XML messages.








 

 

 

 

Top