Thursday, January 11, 2007

Implementing Web Services with Spring and Axis

In the past, I wrote a post on how to implement Web Services using JAX-WS on Glassfish, and Apache Axis. In this post I will describe how to implement Web Services using the Spring framework and Apache Axis. The spring framework uses JAX-RPC API to help implement and access SOAP-WSDL based Web Services. The main components required for implementing and accessing Web Services in Spring are:
  • JaxRpcPortProxyFactoryBean: This is a proxy factory for proxies that communicate with backend Web Services.
  • ServletEndPointSupport: This is the base class for Web Service End Points. A Web Service end point is a class that will be exposed as a Web Service.

Creating the Web Service

The example contains a simple Web Service that echoes back the request message to the client. Follow these steps to imlement the Web Service
  1. Start with a dynamic web project in Eclipse.
  2. The Service Interface: The following is the code for the Service Interface. Nothing new here.
    package service;

    public interface ISpringWS {

    public String sayHello(String message);

    }
    ISpringWS.java
  3. The Service Implementation: The following is the code for the Service Implementation. Again, it's a simple POJO.
    package service;

    public class SpringWS implements ISpringWS {

    public String sayHello(String message) {
    System.out.println("sayHello:" + message);
    return "You said '" + message + "'";
    }
    }
    SpringWS.java
  4. The Service Endpoint: Here is the code for the Service Endpoint, followed by an explanation
    package service;

    import org.springframework.remoting.jaxrpc.ServletEndpointSupport;

    public class SpringWSEndPoint extends ServletEndpointSupport implements ISpringWS {
    private ISpringWS springWS;

    protected void onInit() {
    this.springWS = (ISpringWS) getWebApplicationContext().getBean("springWS");
    }

    public String sayHello(String message) {
    return springWS.sayHello(message);
    }
    }
    SpringWSEndPoint.java

    To implement Web Services using the Spring framework, a service endpoint class has to be written for each service. The service endpoint generally delegates the requests to the Spring-managed beans which implement the actual web service. The service endpoint, however, is not managed by Spring, but by the Web Service too (Axis in our case). Also note that the Service Endpoint implements the Service Interface, since this class acts is the interface to the Web Service.
  5. The server configuration: In order to use Axis as the deployment tool for the web service, you have to add a service section to the Axis server-config.wsdd file. Here is the file in full.
    <?xml version="1.0" encoding="UTF-8"?>
    <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
    <globalConfiguration>
    <parameter name="adminPassword" value="admin" />
    <parameter name="sendXsiTypes" value="true" />
    <parameter name="sendMultiRefs" value="true" />
    <parameter name="sendXMLDeclaration" value="true" />
    <parameter name="axis.sendMinimizedElements" value="true" />
    <requestFlow>
    <handler type="java:org.apache.axis.handlers.JWSHandler">
    <parameter name="scope" value="session" />
    </handler>
    <handler type="java:org.apache.axis.handlers.JWSHandler">
    <parameter name="scope" value="request" />
    <parameter name="extension" value=".jwr" />
    </handler>
    </requestFlow>
    </globalConfiguration>
    <handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler" />
    <handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder" />
    <handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper" />
    <service name="AdminService" provider="java:MSG">
    <parameter name="allowedMethods" value="AdminService" />
    <parameter name="enableRemoteAdmin" value="false" />
    <parameter name="className" value="org.apache.axis.utils.Admin" />
    <namespace>http://xml.apache.org/axis/wsdd/</namespace>
    </service>
    <service name="SpringWS" provider="java:RPC">
    <parameter name="allowedMethods" value="*" />
    <parameter name="className" value="service.SpringWSEndPoint" />
    </service>
    <service name="Version" provider="java:RPC">
    <parameter name="allowedMethods" value="getVersion" />
    <parameter name="className" value="org.apache.axis.Version" />
    </service>
    <transport name="http">
    <requestFlow>
    <handler type="URLMapper" />
    <handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler" />
    </requestFlow>
    </transport>
    <transport name="local">
    <responseFlow>
    <handler type="LocalResponder" />
    </responseFlow>
    </transport>
    </deployment>
    WEB-INF/server-config.wsdd
  6. The spring application context: Here is the applicationContext.xml used for the example
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    <bean id="springWSEndpoint" class="service.SpringWSEndPoint"></bean>
    <bean id="springWS" class="service.SpringWS"></bean>
    </beans>
    WEB-INF/applicationContext.xml
  7. The Web deployment descriptor
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>WSSpring</display-name>
    <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
    <servlet-name>axis</servlet-name>
    <servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
    <load-on-startup>5</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>axis</servlet-name>
    <url-pattern>/axis/*</url-pattern>
    </servlet-mapping>
    </web-app>
    WEB-INF/web.xml
    The Axis servlet definition enables the Axis servlet to make the service available under the given port name.
  8. Jar files: Here is a list of the jar files used for this example
    axis.jarAvailable for download at Apache Axis website
    commons-discovery.jarAvailable with the Spring framework with dependencies download, or here.
    commons-logging.jarAvailable with the Spring framework with dependencies download, or here.
    jaxrpc.jarAvailable with the Spring framework with dependencies download.
    log4j-1.2.13.jarAvailable with the Spring framework with dependencies download, or here.
    saaj.jarAvailable with the Spring framework with dependencies download.
    spring.jarNo need to say where.
    wsdl4j-1.5.1.jarAvailable with the Spring framework with dependencies download.
  9. Deploy and Test: Deploy the application in Weblogic 9.2. You can test the service at the URL
    http://localhost:7001/WSSpring/axis/SpringWS?wsdl

The Web Service Client
  1. Start with a dynamic web project in Eclipse and include all the above jar files.
  2. Service Interface: The service interface can be generated using any of the tools that create Java classes using the WSDL file. For this example, simply copy the ISpringWS.java file into the client application.
    package service;

    public interface ISpringWS {

    public String sayHello(String message);

    }
    ISpringWS.java
  3. The client: The client is a simple Java class, with the service as a member. This will be injected by the spring framework.
    package client;

    import service.ISpringWS;

    public class SpringWSClient {
    ISpringWS springWS;

    public String callService() {
    return springWS.sayHello("Hello");
    }

    public ISpringWS getSpringWS() {
    return springWS;
    }

    public void setSpringWS(ISpringWS springWS) {
    this.springWS = springWS;
    }

    }
    SpringWSClient.java
  4. The Client Servlet:
    package servlets;

    import java.io.IOException;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.WebApplicationContextUtils;
    import client.SpringWSClient;

    public class WSSpringClientServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

    public WSSpringClientServlet() {
    super();
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());
    SpringWSClient sender = (SpringWSClient) ctx.getBean("springWSClient");
    String result = sender.callService();
    response.getWriter().println(result);
    response.getWriter().close();
    }
    }
    WSSpringClientServlet.java
  5. The Application context: You can see the definition of the JaxRpcPortProxyFactoryBean here. This class was described briefly earlier in the post.
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    <bean id="jaxRpcProxy" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
    <property name="serviceFactoryClass">
    <value>org.apache.axis.client.ServiceFactory</value>
    </property>
    <property name="wsdlDocumentUrl">
    <value>http://localhost:7001/WSSpring/axis/SpringWS?wsdl</value>
    </property>
    <property name="namespaceUri">
    <value>http://localhost:7001/WSSpring/axis/SpringWS</value>
    </property>
    <property name="serviceName">
    <value>SpringWSEndPointService</value>
    </property>
    <property name="portName">
    <value>SpringWS</value>
    </property>
    <property name="serviceInterface">
    <value>service.ISpringWS</value>
    </property>

    </bean>
    <bean id="springWSClient" class="client.SpringWSClient">
    <property name="springWS" ref="jaxRpcProxy" />
    </bean>

    </beans>
    WEB-INF/applicationContext.xml

    The service interface used here is a plain Java interface. Using the definition this way will turn service invocations into dynamic JAX-RPC calls (using JAX-RPC's Dynamic Invocation Interface). Another way to implement this is to have the Service interface extend java.rmi.Remote, and have a definition of portInterface.
  6. The web deployment descriptor:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>WSSpringClient</display-name>

    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
    <description></description>
    <display-name>WSSpringClientServlet</display-name>
    <servlet-name>WSSpringClientServlet</servlet-name>
    <servlet-class>servlets.WSSpringClientServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>WSSpringClientServlet</servlet-name>
    <url-pattern>/WSSpringClientServlet</url-pattern>
    </servlet-mapping>


    <welcome-file-list>
    <welcome-file>index.html</welcome-file>

    </welcome-file-list>
    </web-app>
    WEB-INF/web.xml

35 comments:

  1. Thank you so much, these article has helped me a lot.

    Perhaps it would be nice to say that once the war file has been deployed (in Tomcat for example) it is necessary to deploy de WS using the AdminClient of Axis and the wsdd descriptor.

    I use eclipse and the project doesn't deploy the WS like you can do using ant, so after deploying the war file, the WS doesn't work unless you deploy the WS with AdminClient.

    ReplyDelete
  2. Miguel,
    You are probably right (i haven't tried this on Tomcat with eclipse). I however deployed to Weblogic Server 9.2 using the admin client, and no additional steps were required.
    I should probably also mention the deployment environments in the future posts. I'll keep this in mind

    abhi

    ReplyDelete
  3. Hello I am a newcomer in Spring World, and i would to run your example. But it doesn't work: Do i write a jsp file to show the service ? i create the WAR and i put it in the TOMCAT webapp folder, do i do antother thing ? How must i Deploy the WAR ?

    ReplyDelete
  4. cys, I tried this example on Weblogic. But if you want to run it in Tomcat, you have to follow what miguel said in the comment above.

    ReplyDelete
  5. Hai, Abhi,


    Please provide me the materials to implement web services in JBoss Eclipse 3.1 IDE.

    ReplyDelete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Esther

    Hello i'm using Spring-ws whith oc4j v10.1.3 and i cant't connect the client with the service because oc4j don't understan the request form the client. Oc4j don't transform xml's request. What can i do to configure oc4j correctly?

    I hope you can help me.

    Thank you.

    ReplyDelete
  8. Abhi,

    This is amazing example. I need the zip for the whole sample implementation you did with source.
    Is there any link where I can download it.

    Thanks,

    ReplyDelete
  9. I cant get this example working, do I have to create a .wsdl file ? or is it generated automatically in the server ?

    ReplyDelete
  10. ok, i got the example working and deployed and can access the wsdl in the browser but the client example doesnt make any sense how can your client class be injected with a JaxRpcPortProxyFactoryBean when there is no setter ?The bean accessed by the servlet doesnt even use the JaxRpcPortProxyFactoryBean to access the webservice .

    ReplyDelete
  11. how to use this when only getter and setters are used with JaxRpcPortProxyFactoryBean.I dont have a interface and have my service running in axis2 and want to have client in axis with spring.

    ReplyDelete
  12. i have deployed the application on Web logic server 8.1. But i am not able to access the wsdl through the browser by "http://localhost:7001/WSSpring/axis/SpringWS?wsdl" url is there something i am forgetting to do before doing this.
    Please advice as its very urgent.

    ReplyDelete
  13. i'm using the same configuration, i've created a class that extends ServletEndpointSupport, i call the init method, but, every times i call the wsdl it seems that this method is called, instead use a singleton.
    I don't have this class in the applicationContext.xml, i only have this in the server-config.wsdd exposed, i think that it calls init method only once.

    ReplyDelete
  14. Thanks a lot for this post bro...
    I ran this on tomcat without using AdminClient of Axis and it worked. I am not sure why Miguel suggested that?

    ReplyDelete
  15. This is really very useful example.
    we can easily understand the steps.

    Nitesh Patel

    ReplyDelete
  16. Nice article, can you plz share the source code of it,

    thanks.

    Kashif Bashir

    ReplyDelete
  17. Very nice article..I tried it and it worked well. How do you take care of exception handling?

    ReplyDelete
  18. Nice article.. Would be gr8 if you can share the source code also.

    ReplyDelete
  19. Hi Abhi,

    I have a critical problem with the Axis in weblogic9.2. we have two application deployed on the same server one providing the services and the other accessing the service.The functionality is working fine in tomcat server but in weblogic9.2 it throws SocketException:recieved failed.
    If you can give any suggestions it might be helpful for me.

    ~Ravi

    ReplyDelete
  20. I have followed all the steps that you have given in the example in WSAD 5.1.
    I am able to generate the wsdl file also.
    But when I run the servlet to access the deployed service, I go some exception that

    "javax.xml.rpc.ServiceException: Error processing WSDL document:
    javax.xml.rpc.ServiceException: Cannot find service: {http://169.185.141.15:9080/E-W8Project1/axis/SpringWS}SpringWSEndPointService
    Caused by: javax.xml.rpc.ServiceException: Error processing WSDL document:
    javax.xml.rpc.ServiceException: Error processing WSDL document:
    javax.xml.rpc.ServiceException: Cannot find service: {http://169.185.141.15:9080/E-W8Project1/axis/SpringWS}SpringWSEndPointService"

    Please give me some solution so that i can access the webservice through client programm.

    ReplyDelete
  21. I have followed all the steps that you have given in the example in WSAD 5.1.
    I am able to generate the wsdl file also.
    But when I run the servlet to access the deployed service, I go some exception that

    "javax.xml.rpc.ServiceException: Error processing WSDL document:
    javax.xml.rpc.ServiceException: Cannot find service: {http://169.185.141.15:9080/E-W8Project1/axis/SpringWS}SpringWSEndPointService
    Caused by: javax.xml.rpc.ServiceException: Error processing WSDL document:
    javax.xml.rpc.ServiceException: Error processing WSDL document:
    javax.xml.rpc.ServiceException: Cannot find service: {http://169.185.141.15:9080/E-W8Project1/axis/SpringWS}SpringWSEndPointService"

    Please give me some solution so that i can access the webservice through client programm.

    ReplyDelete
  22. Hi Abhi,

    Iam a newbie to webservices. Iam trying to execute this example using MyEclipse. Can you plz give some information how i shld configure Axis in Myeclipse and how will i get the .wsdd file.

    ReplyDelete
  23. Hello All,

    I am trying this tutorial using Eclipse and Tomcat 5.5.27. I've created a WAR file, copied it to the webapps dir and started Tomcat. The WSSpring folder was created and it looks like all the files are there.

    However, when I open the URL http://localhost:8080/WSSpring/axis/SpringWS?wsdl I am getting a 404 error. Any ideas on what I am missing?

    thanks, brian

    ReplyDelete
  24. Alright... I was able to get the WSSpring project up and running in Tomcat, but am having similar issues with WSSpringClientServlet.

    How do you test this and what's expected?

    I imagine it's just: http://localhost:8080/WSSpringClientServlet, but this gives me a 404 error... I don't get it.

    Any help would be much appreciated.

    brian

    ReplyDelete
  25. Hello.

    I have a problem, I try to use this WS in a J2ME aplication and with netBenas dont work, that is the errors:
    SpringWS.wsdl is not compliant with Java ME Web Services specification (JSR-172)

    Can You help me, I realy need to conect that.

    Thanks a lot, and sorry in my english is not the best.

    ReplyDelete
  26. Hi Abhi,

    It was nice tutorial.

    I ahev problem while creating spring ws client

    Snippet of error I got :
    [wldeploy] Task 15 initiated: [Deployer:149026]deploy application springwsclient on AdminServer.
    [wldeploy] weblogic.management.jmx.RemoteRuntimeException: java.rmi.UnmarshalException: failed to unmarshal class java.lang.Object; nested exception is:
    [wldeploy] java.lang.ClassNotFoundException: Failed to load class org.springframework.beans.NotWritablePropertyException
    [wldeploy] at weblogic.management.jmx.MBeanServerInvocationHandler.checkThrowsIOException(MBeanServerInvocationHandler.java:569)
    [wldeploy] at weblogic.management.jmx.MBeanServerInvocationHandler.invoke(MBeanServerInvocationHandler.java:384)
    [wldeploy] at $Proxy8.getTargets(Unknown Source)
    [wldeploy] at weblogic.deploy.api.tools.deployer.Operation.showRawTaskInfo(Operation.java:116)
    [wldeploy] at weblogic.deploy.api.tools.deployer.Operation.showTaskInformation(Operation.java:144)
    [wldeploy] at weblogic.deploy.api.tools.deployer.Jsr88Operation.report(Jsr88Operation.java:535)
    [wldeploy] at weblogic.deploy.api.tools.deployer.Deployer.perform(Deployer.java:140)
    [wldeploy] at weblogic.deploy.api.tools.deployer.Deployer.runBody(Deployer.java:88)
    [wldeploy] at weblogic.utils.compiler.Tool.run(Tool.java:158)
    [wldeploy] at weblogic.utils.compiler.Tool.run(Tool.java:115)
    [wldeploy] at weblogic.Deployer.run(Deployer.java:70)
    [wldeploy] at weblogic.Deployer.mainWithExceptions(Deployer.java:62)
    [wldeploy] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [wldeploy] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    [wldeploy] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    [wldeploy] at java.lang.reflect.Method.invoke(Method.java:585)
    [wldeploy] at weblogic.ant.taskdefs.management.WLDeploy.invokeMain(WLDeploy.java:419)
    [wldeploy] at weblogic.ant.taskdefs.management.WLDeploy.execute(WLDeploy.java:349)
    [wldeploy] at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:275)
    [wldeploy] at org.apache.tools.ant.Task.perform(Task.java:364)
    [wldeploy] at org.apache.tools.ant.Target.execute(Target.java:341)
    [wldeploy] at org.apache.tools.ant.Target.performTasks(Target.java:369)
    [wldeploy] at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1216)
    [wldeploy] at org.apache.tools.ant.Project.executeTarget(Project.java:1185)
    [wldeploy] at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:40)
    [wldeploy] at org.eclipse.ant.internal.ui.antsupport.EclipseDefaultExecutor.executeTargets(EclipseDefaultExecutor.java:32)
    [wldeploy] at org.apache.tools.ant.Project.executeTargets(Project.java:1068)
    [wldeploy] at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.run(InternalAntRunner.java:423)
    [wldeploy] at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.main(InternalAntRunner.java:137)
    [wldeploy] Caused by: java.rmi.UnmarshalException: failed to unmarshal class java.lang.Object; nested exception is:
    [wldeploy] java.lang.ClassNotFoundException: Failed to load class org.springframework.beans.NotWritablePropertyException
    [wldeploy] at weblogic.rjvm.ResponseImpl.unmarshalReturn(ResponseImpl.java:225)
    [wldeploy] at weblogic.rmi.internal.BasicRemoteRef.invoke(BasicRemoteRef.java:224)
    [wldeploy] at javax.management.remote.rmi.RMIConnectionImpl_923_WLStub.getAttribute(Unknown Source)
    [wldeploy] at weblogic.management.remote.common.RMIConnectionWrapper$11.run(ClientProviderBase.java:526)
    [wldeploy] at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:363)
    [wldeploy] at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:147)
    [wldeploy] at weblogic.security.Security.runAs(Security.java:61)


    Did u have any idea of that

    ReplyDelete
  27. Hi,

    I was able to deploy in weblogic 10 and able to run the webservice.
    It would be nice, if you can provide an example, where complex objects are returned.

    If any one knows a link where i can find an example using spring web service client handling complex return types

    ReplyDelete
  28. Hi Abhi,

    Can you please provide some examples where we can pass java objects for web service call.

    Thanks,
    Sharath.

    ReplyDelete
  29. Hi Abhi,

    Modified 'sayHello()' method accepting java bean. Now I'm passing this java bean to 'sayHello' method. The java bean is having first and last name properties.

    When I tried this getting below error

    org.springframework.remoting.RemoteConnectFailureException: Could not connect to remote service [http://localhost:7001/WSSpring/axis/SpringWSSpringWS]; nested exception is java.io.IOException: No serializer found for class com.mycompany.Bean in registry org.apache.axis.encoding.TypeMappingDelegate3ddd9a

    I even posted this error in spring framework forum but no one is answered yet...

    http://forum.springsource.org/showthread.php?t=89266

    Can you please check this problem and let me know, how to resolve this.

    Thanks in advance.

    Regards,
    Sharath.

    ReplyDelete
  30. Thanks for sharing this information. You just save my life. XDDD

    ReplyDelete
  31. Thank you for sharing abhi.
    Above Sample is working fine for me, Follow all the instruction above told by abhi just remove below line in web.xml
    5 and check

    ReplyDelete

Popular Posts