Apache Camel and SOAP

To learn more about Apache Camel I have implemented a non-trivial integration scenario using freely available SOAP web services to create a service that can return the weather at an airport.

The webservices are:

My goals have been:

  • Use web services from different providers
  • Be able to test the routes
  • Include error handling
  • Try various methods for transformation

So far I’m quite happy with the results and impressed with the flexibility of Apache Camel. Have a look at the code available at AirportWeather project @ GitHub and let me know what you think.

Routes

The complete route looks like this:

The flow is synchronous and each step is implemented in a separate route to be testable, for example I can stub out entire steps of the route.

A step can be further broken down into even smaller routes, for example separating the call to the web service and the error handling:

Calling a SOAP web service

To call the web service I use Spring WS. The reason I didn’t use CXF is that the NDFD is using RPC encoding which is an old web standard not supported by the current version of CXF. The XML payload is generated using a Velocity template. This works well in this case, but later I want to compare this to JAXB generated objects from an XML schema.

Transformation

Camel has very flexible support for transformation and un/marshalling. I have tried a couple of different variations. Registering a object and simply calling a method:

Let Camel figure out which method to call:

XML unmarshalling using XStream:

XPath transformation:

Let Camel figure out how to perform the conversion:

Stubbing routes

When testing I used AdviceWith to stub out a route in the test case. I’m not really sure if this is the best way, but it works.

To make the stub actually override the endpoint it is important to call skipSendToOriginalEndpoint. I use Velocity to generate a static reply. See the AirportWeatherTest for more details.

Handling bad results

To verify the result of the invoked web service I have used a choice and rollback to signal the error, for example when the airport does not exist:

Future Camel rides

Things I will investigate further:

  • Other ways to stub out routes
  • Making use of camel-test
  • Use CXF and JAXB to avoid creating XML payloads using Velocity (perhaps for some other web service)

9 Comments

  1. Hi Jan

    This is a nice article. I took the liberty to add a link to it from the Camel articles web page: http://camel.apache.org/articles

    It takes hours for it to sync the update.

  2. Pranay

    HI Jan,
    Nice blog and helped us so much..
    can u please give us full example of calling any existing (external) webservice using camel-cxf and java dsl?

    thanks
    Pranay

  3. Joakim Sundqvist

    Hi Jan

    Excellent article, made a fork of your project and used xsl transformations instead together with xspec for unit-testing the xsl.

    Wrote an article about it here: http://pokerjocke.blogspot.com/2011/09/unit-testing-xsl-transformations-with.html

  4. ramana polaka

    hi jan
    i am new learner of apache camel
    when i ran this app i got exception

    Exception in thread “main” org.apache.camel.CamelExecutionException: Exception occurred during execution on the exchange: Exchange[Message:
    MAA
    MADRAS MENMBARKAM
    India
    IN
    733
    -5.5
    10050
    50
    13
    5
    0
    N
    80
    17
    0
    E
    ]
    at org.apache.camel.util.ObjectHelper.wrapCamelExecutionException(ObjectHelper.java:1368)
    at org.apache.camel.util.ExchangeHelper.extractResultBody(ExchangeHelper.java:622)
    at org.apache.camel.impl.DefaultProducerTemplate.extractResultBody(DefaultProducerTemplate.java:467)
    at org.apache.camel.impl.DefaultProducerTemplate.sendBody(DefaultProducerTemplate.java:133)
    at org.apache.camel.impl.DefaultProducerTemplate.sendBody(DefaultProducerTemplate.java:149)
    at org.apache.camel.impl.DefaultProducerTemplate.requestBody(DefaultProducerTemplate.java:297)
    at com.jayway.airportweather.Main.main(Main.java:16)
    Caused by: com.thoughtworks.xstream.converters.ConversionException: RunwayLengthFeet : RunwayLengthFeet
    —- Debugging information —-
    message : RunwayLengthFeet : RunwayLengthFeet
    line number : 8
    path : /Table/RunwayLengthFeet
    cause-message : RunwayLengthFeet : RunwayLengthFeet
    class : com.jayway.airportweather.model.AirportLocation
    cause-exception : com.thoughtworks.xstream.alias.CannotResolveClassException
    required-type : com.jayway.airportweather.model.AirportLocation
    ——————————-
    at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:45)
    at com.thoughtworks.xstream.core.ReferenceByXPathUnmarshaller.convertAnother(ReferenceByXPathUnmarshaller.java:39)
    at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:99)
    at com.thoughtworks.xstream.core.ReferenceByXPathMarshallingStrategy.unmarshal(ReferenceByXPathMarshallingStrategy.java:12)
    at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:552)
    at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:531)
    at org.apache.camel.dataformat.xstream.AbstractXStreamWrapper.unmarshal(AbstractXStreamWrapper.java:213)
    at org.apache.camel.processor.UnmarshalProcessor.process(UnmarshalProcessor.java:65)
    at org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:72)
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:398)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:191)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:118)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:80)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:191)
    at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:51)
    at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:110)
    at org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:72)
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:398)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:191)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:118)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:80)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:191)
    at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:51)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:191)
    at org.apache.camel.processor.UnitOfWorkProducer.process(UnitOfWorkProducer.java:73)
    at org.apache.camel.impl.ProducerCache$2.doInProducer(ProducerCache.java:378)
    at org.apache.camel.impl.ProducerCache$2.doInProducer(ProducerCache.java:346)
    at org.apache.camel.impl.ProducerCache.doInProducer(ProducerCache.java:242)
    at org.apache.camel.impl.ProducerCache.sendExchange(ProducerCache.java:346)
    at org.apache.camel.impl.ProducerCache.send(ProducerCache.java:201)
    at org.apache.camel.impl.DefaultProducerTemplate.send(DefaultProducerTemplate.java:128)
    at org.apache.camel.impl.DefaultProducerTemplate.sendBody(DefaultProducerTemplate.java:132)
    … 3 more
    Caused by: com.thoughtworks.xstream.alias.CannotResolveClassException: RunwayLengthFeet : RunwayLengthFeet
    at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:35)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.XmlFriendlyMapper.realClass(XmlFriendlyMapper.java:44)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:49)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:46)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:70)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:18)
    at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:27)
    at com.thoughtworks.xstream.converters.reflection.ReflectionConverter.determineType(ReflectionConverter.java:179)
    at com.thoughtworks.xstream.converters.reflection.ReflectionConverter.unmarshal(ReflectionConverter.java:102)
    at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:38)
    … 34 more
    pls help me……….

  5. Frank Walter

    Hello Jan,

    How do I invoke the same SOAP service to get a JSON response instead of xml?
    I have a Rest Route defined, and I want it to invoke a SOAP service to send a JSON payload and receive a JSON response.
    Which component shall I be using? Does Spring WS also solves this? Any example will be highly useful.

    Thanks & Warm Regards,
    Frank Walter

  6. Frank Walter

    Hello Jan,
    Which component we can use to invoke a SOAP service from a camel route and to get a JSON response?
    I have a requirement to write a Camel route that calls a SOAP service and sends a parameter, but the response needs to be JSON?
    Any example would be much helpful.

    Thanks,
    Frank Walter

Leave a Reply