Sunday, November 23, 2008

Consuming web services with Spring

Consuming web services with Spring framework is amazingly easy. It avoids the need of creating client side stubs during compile time and does the same at run time. A typical web service client can be configured as shown below-

<bean id="myWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
    <property name="serviceInterface" value="pkg.MyService"/>
    <property name="wsdlDocumentUrl" value="http://localhost:8080/testws/myService?wsdl"/>
    <property name="namespaceUri" value="http://com.pkg/"/>
    <property name="serviceName" value="MyService"/>
    <property name="portName" value="MyServicePort"/>
</bean>
Where serviceInterface is the business interface that clients will use for invoking service methods. wsdlDocumentUrl is the URL for the published WSDL file. Spring will download WSDL from the given URL and generate the client side stubs when it creates the bean usually during application start up. namespaceUri corresponds to the targetNamespace in the .wsdl file. serviceName corresponds to the service name in the .wsdl file. portName corresponds to the port name in the .wsdl file.

Now accessing web service pretty easy, just get the bean from Spring context and use that to invoke methods-
MyService myService = springContext.getBean("myWebService");
myService.someMethod(. . .);
One downside of this approach is that the web service must be up running when consumer application is being deployed. To avoid this problem one can use lazy-init="true" to lazily initialize the beans when they are first requested but all this quickly becomes unmanageable when we have an array of SOA based applications. In such a scenario one have to strictly follow the order of application deployment else deployment might fail.

31 Comments:

Anonymous said...

Hello Vinod,
I tried using this but no success yet.

I am getting an exception saying:
org.springframework.remoting.jaxws.JaxWsSoapFaultException: System.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: .

Can you please let me know how do I solve this exception?

I am using the example service from http://www.webservicex.com/ValidateEmail.asmx?WSDL.

Thanks
Sri

Thanks & Regards
Srikanth

Anonymous said...

Sri,

If you can show the code, then I may have a look at that.

blackmoon said...

hi vinod ,

i am using the same but getting the Qname not null erorr while creating the Bean.
can you please suggest.

Anonymous said...

Hi Vinod,

I want to ask silly question :D, what is it mean by serviceInterface property, I mean what should I put as the value in there, is it the interface in the web service server or we should create another class in the client as the value?

Thanks,

Anonymous said...

Btw, I wonder with the packaging also...

Thanks,

Anonymous said...

serviceInterface is the interface, based on that JaxWsPortProxyFactoryBean will create a proxy. You need to create it to consume the web service. See here for more details.

Anonymous said...

Owh I think Im missing one step here...CMIIW i have to build jar of my web service first? import is as library in the client, then the serviceInterface will point to that jar, is that correct? I think that spring reference, must be read throughly...T.T

Anonymous said...

Hi Vinod,
I think I am missing something here, the question is also about the Service interface. I am supposed to consume .Net service. I cannot bundle the interface in a jar. Do I need to redefine the C# interface on the Client?
Spring Doc is too vague at least for me. Your help is really appreciated. thanks

Anonymous said...

You need to create that interface and related POJOs manually to consume the service through Spring.

Instead of using Spring, I prefer to consume a web service by creating client side stubs. See step #6 and 7 in Building JAX-WS web service.

Anonymous said...

Hi Vinod,

I did the similar example explained above but i stuck with below error.

Caused by: org.xml.sax.SAXException: Deserializing parameter 'Output': could not find deserializer for type {https://xxx083c1.svr.us.abc.net/UsrControlRoom/wsdl/linkAction}Output
at org.apache.axis.message.RPCHandler.onStartChild(RPCHandler.java:277)
at org.apache.axis.encoding.DeserializationContext.startElement(DeserializationContext.java:1035)

Any idea how to keep deserializer for Output in Spring based WS call?

Thanks,
Laks.

Anonymous said...

Hi,

How to use this example when being behind the HTTP proxy?

Thanks
Maciej

Anonymous said...

@Maciej,

Proxy authentication in Java may be useful to you.

Maciej said...

Thanks,

I tried this but it doesn't work for me when trying to call a web service method. I get:

org.apache.cxf.interceptor.Fault: Could not send Message.

followed by

Caused by: java.net.HttpRetryException: cannot retry due to proxy authentication, in streaming mode
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1010)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:373)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1896)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1824)
at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:66)
at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:583)
at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)

Any ideas?

Thanks
Maciej

kaushal bhati said...

nice....i have done example and running fine...great.............

Laxmipathi said...

Hi Kosal

If possible can you please share your code, even i tried this example with http://www.webservicex.com/ValidateEmail.asmx?WSDL but its not working.

kaushal bhati said...

ya sure laxmipathi...

Anonymous said...

Hi Vinod,

As per your guidelines i was trying to consume the web service and i am getting Null pointer Exception. Please have a look

SEVERE: Exception sending context initialized event to listener instance of class de.hybris.platform.spring.HybrisContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'integrationTesting' defined in ServletContext resource [/WEB-IN
Invocation of init method failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1403)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:290)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:287)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:545)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:871)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:423)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:272)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:196)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3934)

Anonymous said...

can pls post the "pkg.MyService" file also

Anonymous said...

That you need to create. This is the service definition interface created based on WSDL.

semaj said...

The lazy-init="true" didn't work for me. I saved a local copy of the wsdl file and modified the wsdlDocumentUrl and pointed to the local copy. Then, I added .

The endpointAddress overrided the one specified in the local wsdl file.

If I don't do this, my local dev server won't start when the remote dev server is not online.

semaj said...

The blogger block the code snippet I wrote.
This is what I added.
property endPointAddress value=wsdl location

Hope this will help for someone.

Thanks

bhaus said...

Hi Vinod,

I have tried to implement WS using the Spring but getting the error.

Error is Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'emailValidateService' defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is class: net.webservicex.IsValidEmail could not be found

Here is my code.

@WebService(name = "ValidateEmailSoap", targetNamespace = "http://www.webservicex.net")
@XmlSeeAlso({
ObjectFactory.class
})

public interface EmailValidateService {

/**
* This method returns true or false for a given email address
*
* @param email
* @return
* returns boolean
* http://www.webservicex.com/ValidateEmail.asmx
*/

@WebMethod(operationName = "IsValidEmail", action = "http://www.webservicex.net/IsValidEmail")
@WebResult(name = "IsValidEmailResult", targetNamespace = "http://www.webservicex.net")
@RequestWrapper(localName = "IsValidEmail", targetNamespace = "http://www.webservicex.net", className = "net.webservicex.IsValidEmail")
@ResponseWrapper(localName = "IsValidEmailResponse", targetNamespace = "http://www.webservicex.net", className = "net.webservicex.IsValidEmailResponse")
public boolean isValidEmail(
@WebParam(name = "Email", targetNamespace = "http://www.webservicex.net")
String email);


}

applicationContext.xml











Test.java

public class Test {
private static ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
private static EmailValidateService service = (EmailValidateService) context.getBean("emailValidateService");


public static void main(String[] args) {
try{
System.out.println(service.isValidEmail("support@mks.com"));
}catch(Exception e) {
e.printStackTrace();
}
}

}

Anonymous said...

@Bhaus,

The exception is very clear from the error message "net.webservicex.IsValidEmail could not be found". Do you have this class in your classpath ?

bhaus said...

No I dont have that class
in my classpath.
I'm not accessing my local WS. I'm accessing the Third party WSDL.

WSDL URL:
http://www.webservice
x.com/ValidateEmail.asmx?WSDL


Here is my ApplicationContext.xml content

bhaus said...

sorry dont know why applicationcontext.xml content is not displaying
I'm pasting again.

Next Generation JEE said...

vinod/kosal,

I need to consume the webservice using spring,as per urs i tried and got the Excpetion: A WebService annotation is not present on class

do we need to create stubs manually?

kosal, can u send me a executed code to my mail. sveerakumar@gmail.com

Thanks,
veera

New Mexico Service Annabelle said...

Hi, Kosal!

Please send me your code too. Tried this and it's not working properly yet. Would appreciate the help.

annabelle [at] ardham [dot] com

Thank you!

HarpreetSingh said...

Hi Vinod

I am trying to intercept the request and response SOAP messages with the configuration you have specified.

Can you please tell me on how to do this?

Regards

Vinod Singh said...

@Harpeet, What do you meant by interception? If you want to see the SOAP messages being interchanged between client and server then try Soap / http Monitoer. SoapUI is also a good option. In case you want to do some operations even before service is invoked then SOAP handler are the way to go.

HarpreetSingh said...

I need to log the SOAP request and response in the Log file. I am able to do this for the services I am creating using the SoapEnvelopeLoggingInterceptor however this works only for the service endpoints created with @Endpoint and @PayloadRoot annotations.

I have a third party wsdl which I am consuming as per the method stated by you and need to log the request and response in Log file. Please help.

Samuel Sosa said...

Hi Vinod,

Im having this exeption when trying to consume a WebMethod:

com.sun.xml.ws.fault.ServerSOAPFaultException: Client received SOAP Fault from server: Cannot find dispatch method for .....

Is like is not getting the methods from my wsdl.

Any suggestion?