Forum Home » Fuse Distributions » Fuse Mediation Router

Thread: How to create a consumer endpoint for a camel route using SOAP

 

Permlink Replies: 14 - Last Post: Nov 2, 2010 6:56 PM Last Post By: valig003
rkozura

Posts: 29
Registered: 08/02/10
How to create a consumer endpoint for a camel route using SOAP
Posted: Aug 18, 2010 7:58 PM
  Click to reply to this thread Reply
I have a question. I am new to Camel.

I have created a very simple camel route using the timer scheme to route a message from the timer to an nmr endpoint that accepts SOAP (jaxws )messages. I now want to go a step further.

I want to be able to expose an http endpoint on the ESB to accept SOAP messages and route the message to the nmr endpoint. I have found a way to expose some sort of http endpoint using the jetty scheme, but when I send a SOAP request to the endpoint, I get the following error:

ERROR: 'Source object passed to ''{0}'' has no contents.'

This leads me to believe that jetty is not SOAP enabled.

I'll stop pretending to know what I am talking about :) Am I correct in using jetty as the http consumer? Is there another scheme that could be used? I find it strange that the jetty endpoint can have a different address as it sits in the ESB.

Here is my camel route:

<camel-osgi:camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="jetty://http://localhost:8181/camel" />
<to uri="nmr:SOAPProxy"/>
</route>
</camel-osgi:camelContext>
ffang

Posts: 1,320
Registered: 12/24/07
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Aug 19, 2010 1:51 AM   in response to: rkozura in response to: rkozura
  Click to reply to this thread Reply
Hi,

This error means you need transform you message to some kind of Source(such as StringSource) before you send to nmr endpoint.

Freeman
rkozura

Posts: 29
Registered: 08/02/10
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Aug 19, 2010 1:28 PM   in response to: ffang in response to: ffang
  Click to reply to this thread Reply
Thank you! I will try that soon and edit this post about it :)
rkozura

Posts: 29
Registered: 08/02/10
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Aug 19, 2010 6:50 PM   in response to: rkozura in response to: rkozura
  Click to reply to this thread Reply
I transformed the message, but am still having problems. The response is not going back to jetty. The SOAP message does indeed go to correct location, that I am thankful for.

Here is my route:

<camel-osgi:camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="jetty://http://localhost:8181/camel" />
<bean ref="myTransform" method="transform"/>
<to uri="nmr:SOAPProxy"/>
</route>
</camel-osgi:camelContext>

Here is the transform method:

public Object transform(Source body) {

return body;
}

A message is displayed by my web service stating the message was handled, but then I get the dreaded ArrayIndexOutOfBoundsException:

java.lang.ArrayIndexOutOfBoundsException: 4096
at org.mortbay.io.ByteArrayBuffer.poke(ByteArrayBuffer.java:268)
at org.mortbay.io.AbstractBuffer.put(AbstractBuffer.java:456)
at org.mortbay.jetty.HttpFields$Field.put(HttpFields.java:1424)
at org.mortbay.jetty.HttpGenerator.completeHeader(HttpGenerator.java:523)
at org.mortbay.jetty.HttpConnection.commitResponse(HttpConnection.java:632)
at org.mortbay.jetty.HttpConnection$Output.flush(HttpConnection.java:1004)
at org.apache.camel.util.IOHelper.copy(IOHelper.java:101)
at org.apache.camel.util.IOHelper.copy(IOHelper.java:81)

Thank you so much for your help so far!

Edit: Seems to be an issue with my Jetty version. How do I update Jetty that is in FUSEESB? :)

Edited by: rkozura on Aug 19, 2010 6:52 PM

Edited by: rkozura on Aug 19, 2010 7:16 PM
ffang

Posts: 1,320
Registered: 12/24/07
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Aug 20, 2010 1:33 AM   in response to: rkozura in response to: rkozura
  Click to reply to this thread Reply
Hi,

How about append option like exchangePattern=InOut to your jetty uri?

Freeman
rkozura

Posts: 29
Registered: 08/02/10
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Aug 20, 2010 2:42 PM   in response to: ffang in response to: ffang
  Click to reply to this thread Reply
Like a game of poker: No dice. :(
rkozura

Posts: 29
Registered: 08/02/10
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Aug 20, 2010 2:48 PM   in response to: ffang in response to: ffang
  Click to reply to this thread Reply
Is there any other endpoint I can expose besides Jetty? All I want is an http endpoint consumer that uses camel.
valig003

Posts: 11
Registered: 10/20/10
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Oct 27, 2010 2:24 PM   in response to: rkozura in response to: rkozura
  Click to reply to this thread Reply
I have a similar problem and get the same error. I have a CXF-based web service bundle that uses an OSGi bundle that provides database connectivity. I want to use the Camel "throttle" pattern to avoid stressing the (production) database as a stop-gap.

Jetty/HTTP -> Camel Throttle -> CXF WebService (SOAP) -> OSGi Database Bundle -> JDBC Pool -> Database

I wasn't able to get it to work until I added the "convertBodyTo" tag as suggested here. With this tag, the service itself works great and throttles correctly, however I can no longer access the WSDL without the "Source object passed to ''{0}'' has no contents" error as reported in this post. I assume this is because the body of a WSDL request is empty, but it seems like something the convertBodyTo should handle more elegantly. I've tried putting error handling around it as well without any luck. Any suggestions for how to get a WSDL operation to work without this error? Like I said, the actual SOAP call to the service performs perfectly.

Here is part of my Spring setup:

...

<!-- Throttle setting for this service -->
<camel:camelContext id="throttleRoute" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="jetty:http://localhost:8181/cxf/EmailService" />
<convertBodyTo type="javax.xml.transform.stream.StreamSource" />
<throttle maximumRequestsPerPeriod="1" timePeriodMillis="10000">
<to uri="nmr:EmailService" />
<*/throttle*>
</route>
</camel:camelContext>

<bean name="emailServiceBean" class="edu.umn.arch.service.impl.EmailServiceImpl">
<property name="emailDataAccessor" ref="emailDAO" />
</bean>

<jaxws:endpoint id="emailWSDLService"
implementor="#emailServiceBean"
address="nmr:EmailService" />

...

Edited by: valig003 on Oct 27, 2010 2:31 PM

Edited by: valig003 on Oct 27, 2010 2:37 PM
davsclaus

Posts: 1,893
Registered: 10/14/08
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Oct 28, 2010 4:29 AM   in response to: valig003 in response to: valig003
  Click to reply to this thread Reply
Try enabling stream caching instead of the convert body to.

http://camel.apache.org/stream-caching.html
valig003

Posts: 11
Registered: 10/20/10
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Oct 28, 2010 7:27 PM   in response to: davsclaus in response to: davsclaus
  Click to reply to this thread Reply
Thanks for the suggestion, but this didn't affect the behavior. Removing the "convertBodyTo" node caused errors regardless of stream-cache setting. It also didn't change the behavior with both stream-cache AND "convertBodyTo".

Putting the "convertBodyTo" back allows SOAP calls with a message body to succeed, but WSDL calls to fail.

StackTrace
org.apache.servicemix.nmr.api.ServiceMixException: javax.xml.transform.TransformerException: javax.xml.transform.TransformerException: Source object passed to ''{0}'' has no contents.
at org.apache.servicemix.cxf.transport.nmr.NMRDestination.process(NMRDestination.java:151)278:org.apache.servicemix.cxf.transport.nmr:4.3.0.fuse-02-00
at org.apache.servicemix.nmr.core.InternalEndpointWrapper.process(InternalEndpointWrapper.java:86)81:org.apache.servicemix.nmr.core:1.3.0.fuse-01-00
at org.apache.servicemix.nmr.core.ChannelImpl.process(ChannelImpl.java:255)81:org.apache.servicemix.nmr.core:1.3.0.fuse-01-00
at org.apache.servicemix.nmr.core.ChannelImpl$1.run(ChannelImpl.java:215)81:org.apache.servicemix.nmr.core:1.3.0.fuse-01-00
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886):1.6.0_17
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908):1.6.0_17
at java.lang.Thread.run(Thread.java:619):1.6.0_17
Caused by: javax.xml.transform.TransformerException: javax.xml.transform.TransformerException: Source object passed to ''{0}'' has no contents.
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:720):1.6.0_17
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:313):1.6.0_17
at org.apache.servicemix.cxf.transport.nmr.NMRMessageHelper.convertMessageToInputStream(NMRMessageHelper.java:51)278:org.apache.servicemix.cxf.transport.nmr:4.3.0.fuse-02-00
at org.apache.servicemix.cxf.transport.nmr.NMRDestination.process(NMRDestination.java:127)278:org.apache.servicemix.cxf.transport.nmr:4.3.0.fuse-02-00
... 6 more
Caused by: javax.xml.transform.TransformerException: Source object passed to ''{0}'' has no contents.
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:668):1.6.0_17
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:708):1.6.0_17
... 9 more
davsclaus

Posts: 1,893
Registered: 10/14/08
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Oct 29, 2010 8:03 AM   in response to: valig003 in response to: valig003
  Click to reply to this thread Reply
What do you mean by WSDL call?
valig003

Posts: 11
Registered: 10/20/10
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Oct 29, 2010 3:49 PM   in response to: davsclaus in response to: davsclaus
  Click to reply to this thread Reply
Sorry, I didn't want to completely hi-jack the original post here, but to answer your question:

Without Camel, a CXF service is generally accessed at http://{some.host}/cxf/ServiceName. The WSDL for that service is accessed at http://{some.host}/cxf/ServiceName?wsdl. This is the endpoint that defines the service contract and would be given out to clients to let them call the service.

For my service, the following URLs work great until I add Camel:
http://localhost:8181/cxf/EmailService?wsdl
http://localhost:8181/cxf/EmailService (for the SOAP call)

Once the Camel throttle route listed above is added, I start getting the error "javax.xml.transform.TransformerException: Source object passed to ''{0}'' has no contents" for BOTH the WSDL and SOAP calls. By adding the "convertBodyTo" call, the SOAP call works great and the throttling in Camel is awesome... but the call to retrieve the WSDL still comes back with the error, presumably because the body is empty in a WSDL call; seems like the transform that "convertBodyTo" uses can't handle an empty body.

I'm looking for a way to make both of these work for the Camel throttle - or a workaround if something is broken in Camel.

Thanks!
njiang

Posts: 572
Registered: 09/17/07
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Nov 2, 2010 11:33 AM   in response to: valig003 in response to: valig003
  Click to reply to this thread Reply
You may need to use other way to server the ?wsdl request, like using <choice> to check the query string then send the request to cxf endpoint without use transformation.
valig003

Posts: 11
Registered: 10/20/10
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Oct 29, 2010 10:44 PM   in response to: valig003 in response to: valig003
  Click to reply to this thread Reply
I tried a different approach but no longer get the Camel route to work. I tried removing the "nmr" references and instead hard coded three URI references... both the SOAP call and the WSDL worked and there was no need for the convertBodyTo call, but the Camel throttle no longer executed (no error, just didn't get called).

The confusing part is that there seem to be MANY ways to define a 'service' endpoint and they all have different behavior and visibility (osgi:service, cxf:cxfEndpoint, jaxws:endpoint, cxf:simple, camel:endpoint, ...). Determining what protocols, URIs, and address settings to use is very fuzzy. I haven't found a good example using Spring, CXF with the jaxws:endpoint tag, being called by Camel.

For reference, here is my Spring config for deploying a CXF service via the jaxws endpoint... how do I get Camel to work with this service endpoint?

<!-- Throttle setting for this service -->
<camel:camelContext streamCache="false" id="throttleRoute" xmlns="http://camel.apache.org/schema/spring">
<route streamCache="false">
<from uri="jetty:http://localhost:8181/cxf/EmailService" />
<throttle maximumRequestsPerPeriod="1" timePeriodMillis="10000">
<to uri="http://localhost:8181/cxf/EmailService" />
</throttle>
</route>
</camel:camelContext>

<bean name="emailServiceBean" class="edu.umn.arch.service.impl.EmailServiceImpl">
<property name="emailDataAccessor" ref="emailDAO" />
<property name="testMode" value="${testMode}" />
</bean>

<jaxws:endpoint id="emailWSDLService"
implementor="#emailServiceBean"
address="http://localhost:8181/cxf/EmailService" />
valig003

Posts: 11
Registered: 10/20/10
Re: How to create a consumer endpoint for a camel route using SOAP
Posted: Nov 2, 2010 6:56 PM   in response to: valig003 in response to: valig003
  Click to reply to this thread Reply
Well, a little progress... the high level issue seems to be making Camel aware of CXF and allowing the WSDL call to pass through. With some changes I've made, I now get a message related to "Can't find input stream in message".

In researching this, there is an open Camel issue that was just addressed yesterday that seems to be the same issue: https://issues.apache.org/activemq/browse/CAMEL-3269

I'm not sure how anyone could ever know all of the tweaks that are necessary to make this work, but here is my current spring file in hopes that it might help someone else (or that someone might see a mistake and set me straight!):

<?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:osgi="http://www.springframework.org/schema/osgi"
xmlns:camel="http://camel.apache.org/schema/spring"
xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
http://www.springframework.org/schema/osgi-compendium http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-http-jetty.xml" />
<import resource="classpath:META-INF/cxf/transport/nmr/cxf-transport-nmr.xml" />
<import resource="classpath:META-INF/cxf/osgi/cxf-extension-osgi.xml" />
<import resource="classpath:org/apache/servicemix/camel/nmr/camel-nmr.xml" />

<!-- Throttle setting for this service -->
<camel:camelContext streamCache="false" id="throttleRoute" xmlns="http://camel.apache.org/schema/spring">
<route streamCache="false">
<from uri="jetty:http://localhost:8181/cxf/EmailService" />
<throttle maximumRequestsPerPeriod="1" timePeriodMillis="10000">
<to uri="direct:EmailService" />
</throttle>
</route>
</camel:camelContext>

<!-- Make Camel CXF-aware -->
<bean class="org.apache.camel.component.cxf.transport.CamelTransportFactory">
<property name="bus" ref="cxf" />
<property name="camelContext" ref="throttleRoute" />
<property name="transportIds">
<list>
<value>http://cxf.apache.org/transports/camel</value>
</list>
</property>
</bean>

<!-- Service bean with DAO reference and property injection -->
<bean name="emailServiceBean" class="edu.umn.arch.service.impl.EmailServiceImpl">
<property name="emailDataAccessor" ref="emailDAO" />
<property name="testMode" value="${testMode}" />
</bean>

<jaxws:endpoint id="emailWSDLService"
implementor="#emailServiceBean"
address="camel://direct:EmailService" />
<!-- URI: publishedEndpointUrl="http://${host}/EmailService" -->

<!-- Properties (can be overridden at runtime in a cfg file) -->
<osgix:cm-properties id="instProps" persistent-id="edu.umn.arch.service.email">
<prop key="testMode">true</prop>
</osgix:cm-properties>
</beans>