Monday, November 30, 2009

Proxying SOAP Web Services in JBossESB 4.7

JBossESB supports SOAP web services via various mechanisms:
  • SOAPClient for invoking external web services.
  • SOAPProducer for invoking internally-deployed JBossWS services.
  • EBWS (ESB-Based Web Services) for decorating service requests/responses with a SOAP Envelope.
  • HttpRouter can be used to manually invoke an HTTP endpoint.
  • Explicit support for web service proxying via the SOAPProxy action.
Why would I want to proxy a web service from the ESB?
  • To provide loose coupling between the client and the proxied service: the client no longer needs to have a direct connection to the remote host.
  • The WSDL contract can be transformed to modify it's parameters or change the service's declared capabilities.
  • A transformation of the SOAP envelope/body can be introduced via the ESB action chain both for the inbound request and outbound response (via XsltAction or SmooksAction). Other custom processing can also be introduced in the chain.
  • Service versioning is possible since clients can connect to 2 or more proxy endpoints on the ESB, each with its own WSDL and/or transformations and routing requirements, and the ESB will send the appropriate message to the appropriate endpoint and provide an ultimate response.
  • Complex context-based routing via ContentBasedRouter.
Here is a figure of a basic web service proxy in the ESB:


As you can see, the ESB action chain can perform any number of out-of-the-box or custom actions before or after the call to the proxied web service. Also, the client of the ESB as well as the proxied web service do not have to be Java-based.

Show me how to configure the ESB to proxy to a .NET-based SOAP web service.

Here it is in 15 short lines:
1:  <?xml version="1.0" encoding="UTF-8"?>
2: <jbossesb xmlns="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.2.0.xsd" parameterReloadSecs="5">
3: <services>
4: <service category="Stock" name="Quote" description="Stock Quote" invmScope="GLOBAL">
5: <listeners>
6: <http-gateway name="StockQuote-GwListener"/>
7: </listeners>
8: <actions mep="RequestResponse">
9: <action name="proxy" class="org.jboss.soa.esb.actions.soap.proxy.SOAPProxy">
10: <property name="wsdl" value="http://www.webservicex.net/stockquote.asmx?WSDL"/>
11: </action>
12: </actions>
13: </service>
14: </services>
15: </jbossesb>

Line 8 specifies a request/response message exchange pattern. Line 9 adds the SOAPProxy action to the processing pipeline. Line 10 is the only required property: the wsdl location. In this example, we are proxying to a .NET-based stock quote service. Line 6 specifies the use of the newly supported HTTPGateway, introduced in JBossESB 4.7.

When a .esb archive is deployed with this configuration, you will see output like this in the server console:
07:17:51,789 INFO [SOAPProxy] mapped soapaction ["http://www.webserviceX.NET/GetQuote"] to binding [{http://www.webserviceX.NET/}StockQuoteSoap]
07:17:51,790 INFO [SOAPProxy] mapped binding [{http://www.webserviceX.NET/}StockQuoteSoap] to transport [org.jboss.soa.esb.actions.soap.proxy.HttpSOAPProxyTransport] with endpoint address: [http://www.webservicex.net/stockquote.asmx]

At this point, the SOAPProxy has transformed the wsdl by rewriting it's soap:address location to the ESB server, so clients of the ESB will invoke it rather than the proxied service itself (line 3 below):
1:  <wsdl:service name="StockQuote">
2: <wsdl:port binding="tns:StockQuoteSoap" name="StockQuoteSoap">
3: <soap:address location="http://192.168.1.103:8080/sample/http/Stock/Quote"/>
4: </wsdl:port>
5: </wsdl:service>

With the introduction of the new HTTPGateway, the wsdl is available at the well-known location of the endpoint URL + "?wsdl" (or "?WSDL"), as shown in the following screenshot of the ESB contract application, available at http://host:port/contract/

In our case, the wsdl URL is http://192.168.1.103:8080/sample/http/Stock/Quote?wsdl
To invoke the ESB, we can use a tool like soapUI to send a test request to http://192.168.1.103:8080/sample/http/Stock/Quote
1:  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.webserviceX.NET/">
2: <soapenv:Header/>
3: <soapenv:Body>
4: <web:GetQuote>
5: <web:symbol>RHT</web:symbol>
6: </web:GetQuote>
7: </soapenv:Body>
8: </soapenv:Envelope>

The HTTPGateway will receive the request, the SOAPProxy action will invoke the proxied .NET stock quote service, and the following response will be returned:
1:  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2: <soap:Body>
3: <GetQuoteResponse xmlns="http://www.webserviceX.NET/">
4: <GetQuoteResult><![CDATA[<StockQuotes><Stock><Symbol>RHT</Symbol><Last>26.80</Last><Date>11/27/2009</Date><Time>1:00pm</Time><Change>0.00</Change><Open>N/A</Open><High>N/A</High><Low>N/A</Low><Volume>0</Volume><MktCap>5.032B</MktCap><PreviousClose>26.80</PreviousClose><PercentageChange>0.00%</PercentageChange><AnnRange>8.30 - 28.94</AnnRange><Earns>0.448</Earns><P-E>59.82</P-E><Name>RED HAT INC</Name></Stock></StockQuotes>]]></GetQuoteResult>
5: </GetQuoteResponse>
6: </soap:Body>
7: </soap:Envelope>

Why can't I just use HttpRouter to do this?
  1. HttpRouter can only talk to one endpoint. One WSDL can specify multiple soap:address locations. With the SOAPProxy, you just config the one location of the WSDL, and it can dynamically route the request to the correct soap:address location.
  2. To use HttpRouter with SOAP, you have to always configure a MappedHeaderList (usually "Content-Type, Accept, Authorization, SOAPAction"). This is unnecessary with the SOAPProxy.
  3. The SOAPProxy allows you to specify a wsdlTransform, which allows you to transform the original WSDL to something else you want the SOAPProxy to expose. (This handles the "versioning" use case, and also usually means you'll want to use XsltAction or SmooksAction in the action pipeline.)
  4. If you are proxying to a BASIC Auth secured web service, the "clientCredentialsRequired" property of the SOAPProxy allows you to specify if the credentials should be passed into the SOAPProxy from the client (the default, in which case the credentials are propagated), or if you want the proxy to be wide-open, but it will handle the authentication to the proxied service for you.
  5. SOAPProxy is designed to talk to more than just HTTP endpoints, although HTTP is all that is implemented right now.
  6. When using the new HTTPGateway to front the SOAPProxy, the ESB will automatically transform and cache the WSDL and make it available at the well-known location of ${endpoint}?wsdl (or ?WSDL).
Tell me more about security.

As mentioned in #4 above, the SOAPProxy can invoke external web services that are protected by BASIC Auth. In addition, these services can also be encrypted using SSL. Here is an example configuration taken from the webservice_proxy_security quickstart which comes bundled with JBossESB 4.7:
1:  <?xml version="1.0" encoding="UTF-8"?>
2: <jbossesb xmlns="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.2.0.xsd" parameterReloadSecs="5">
3: <globals>
4: <war-security method="BASIC" domain="JBossWS"/>
5: </globals>
6: <providers>
7: <http-provider name="HTTP-PROVIDER">
8: <http-bus busid="HTTP-BUS" transportGuarantee="CONFIDENTIAL">
9: <allowed-roles>
10: <role name="friend"/>
11: </allowed-roles>
12: </http-bus>
13: </http-provider>
14: </providers>
15: <services>
16: <service category="Proxy_Security" name="Proxy" description="Security WebService Proxy" invmScope="GLOBAL">
17: <listeners>
18: <http-gateway name="HTTP-GATEWAY" busidref="HTTP-BUS" urlPattern="ProxyWS/*"/>
19: </listeners>
20: <actions mep="RequestResponse">
21: <action name="proxy" class="org.jboss.soa.esb.actions.soap.proxy.SOAPProxy">
22: <!-- property name="wsdl" value="https://localhost:8443/webservice_proxy_security/HelloWorldWS?wsdl"/ -->
23: <property name="wsdl" value="internal://jboss.ws:context=webservice_proxy_security,endpoint=HelloWorldWS"/>
24: <property name="endpointUrl" value="https://localhost:8443/webservice_proxy_security/HelloWorldWS"/>
25: <property name="file" value="/META-INF/httpclient-8443.properties"/>
26: </action>
27: </actions>
28: </service>
29: </services>
30: </jbossesb>
Lines 3-14 configure the HTTPGateway for BASIC Auth and SSL support. Line 24 overrides the endpointUrl. This is an example of an HttpRouter property (Note: the SOAPProxy wraps usage of HttpRouter), a useful property when domain name matching is important for SSL certs. Line 25 specifies a properties file for httpclient configuration.

What's with the "internal" wsdl location?

Line 22 (a "normal" http URL) is commented out and instead, on Line 23, you see an "internal" URL. In JBossESB 4.7, the value can reference a location based on five different schemes: http, https, file, classpath or internal (JBossWS jmx mbean object name). Here are some examples:
http://localhost:8080/Quickstart_webservice_proxy_basic_ws/HelloWorldWS?wsdl
https://localhost:8443/webservice_proxy_security/HelloWorldWS?wsdl
file:///tmp/HelloWorldWS.wsdl
classpath://META-INF/HelloWorldWS.wsdl
internal://jboss.ws:context=Quickstart_webservice_proxy_basic_ws,endpoint=HelloWorldWS

I want to learn more...

Other quickstart samples available are webservice_proxy_basic, webservice_proxy_routed (an example using the ContentBasedRouter to route to one of two proxied endpoints) and webservice_proxy_versioning (an example showing support for old and new versions of the same endpoint via transformation).

Of course, you are always welcome to ask questions in the JBossESB project forum.

20 comments:

Daniel Bevenius said...

Nice blog post David!

Unknown said...

Toward the end of your post, you state that the 'wsdl' property can support up to 5 schemes, including 'classpath://'. Your example of the classpath scheme (classpath://META-INF/HelloWorldWS.wsdl) does not work.

In stepping through the ESB code, we find that the ClassUtil object parses this value by extracting everything after the 'classpath://' and then examining the first character. If the first character is a '/', it gets removed from the path. Otherwise, the path gets preprended with the package name of the object that called on ClassUtil. In other words, your example would be interpreted as 'org.jboss/esb/whatever/META-INF/HelloWorldWS.wsdl', which may be useful for some internal purpose, but is generally stupid for joe client.

So, a correct example would be 'classpath:///META-INF/HelloWorldWS.wsdl'. Notice the extra '/' in there.

Shaun Williams said...
This comment has been removed by the author.
benefit said...
This comment has been removed by the author.
benefit said...

maybe as a little help for beginners you should add that the path to the proxied webservices gets combinded through:

protocol://jbosesb:port/applicationame/servicecategory/servicename/

benefit said...

sorry ...

that would have been correct:

protocol://jbosesb:port/:applicationame:/http/:servicecategory:/:servicename:/


: : Parts need to be replaced with your own configurated values, servicename and category are from the jboss-esb.xml

Gary said...

I know this is an old post but I wanted to ask.. how do you get the WSDL published when using the CBR in front of the SOAPProxy? The SOAProxy automatically produces the WSDL when using the HTTP GW.. This works great.. But I have the CBR in front of them and when I tried to put a inXSD on the Actions node the deployment failed: java.lang.UnsupportedOperationException..

Is this possible??

thanks!

Fabio Martinez Giraldo said...

Hello I would like to know if can post some complete example on how to bind an already created web service with JBoss ESB, as the documentation is incomplete and need it urgently, thank you.

Norma said...

This is an excellent post. I admire your efforts. I will come again to check new updates. Thanks for sharing it.
custom dissertation

Jacob Hubbard said...

I am very happy to learn this article because I was searching for this information for many days. I admire your work. I will bookmark your website. Thanks for sharing.
dissertation service

Unknown said...

This is an excellent post. Keep on posting such informative articles. I praise your work. I will come again to read new articles. Thank you.
essay topic ideas

Curvve said...

Thanks for the sharing this website. it is very useful professional knowledge. Great idea you know about
company background.

Secure Web Design

Unknown said...

I am hoping that by my next post here, I will have the links up to Book Locker, as well as Amazon, where this new novel will be available for sale.
Dissertation Samples

Anonymous said...

On this resource always publish the most interesting information, thanks to it it is visited by a great number of people that provides quality of articles. For example when I read this article I at once defined that the author understands the considered subject and has at least profile education on it. I thank edition and the author for their work which is always qualitative, actual and interesting to a wide range of readers.
play online slots

Albert Barkley said...

Don't you think that proxying is not good for corporate level as just a little bit mistake (spamming) can ban whole IP from internet. Dissertation writing services

Unknown said...

I am confident they will be benefited from this site.Thanks a lot for sharing. Keep blogging. thanks  
Cheap Dissertation Writing Services

Chris Greenwalty said...

Good one methodology used for programming. Thanks for sharing useful post. Cheap dissertation writing services

Ashley Sewell said...

In my opinion proxying web services can make sever authentication and security pretty low making it suitable for hackers. Dissertation writing service

Unknown said...

Your blog is very informative and unique. I admire your efforts. Thanks for sharing this information.Affordable Driving School

Should I Enroll My Child in Driving Lessons said...

Great site you have here but I was wanting to know if you knew of any discussion boards that cover

Driving School Mill Park
Driving school Watsonia
Driving school Greensborough