Announcement Announcement Module
Collapse
No announcement yet.
Spring RESTful Client/Server tutorial Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring RESTful Client/Server tutorial

    Hi all,

    I am looking to see if anyone knows of any good blogs/tutorials on exposing RESTful URL's both on the server and then communicating with them on a client using Spring 3?

    I am comfortable with Spring MVC and the various annotations and after reading the following:

    http://blog.springsource.com/2009/03...-spring-3-mvc/
    http://blog.springsource.com/2009/03...-resttemplate/

    I was wondering if anyone could point out a full blown example? Or is there an example of such a thing in the Spring 3 distribution?

    Many thanks in advance for the pointers

    Eggsy

  • #2
    In brief:


    Server uses default annotated controllers with @PathVariable annotation:
    Code:
    @Controller
    public class MyController {
    
        @Autowired
        Service myService;
    
        @RequestMapping(value="/path/{word}", method=RequestMethod.GET)
        public ModelAndView myRestMethod(@PathVariable String word) {
    
            ResponseObj response = myService.getResponse(word);
            return new ModelAndView("jaxbView", BindingResult.MODEL_KEY_PREFIX + "response", response);
        }
    }
    The controller requires a view named "jaxbView" and a viewresolver to resolve the same:
    Code:
        <!-- Scan for controllers -->
        <context:component-scan base-package="my.controller.package" />
    
    
        <!-- Resolve views based on string names -->
        <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
    
    
        <!-- XML view using a JAXB2 marshaller -->
        <bean id="jaxbView" class="org.springframework.web.servlet.view.xml.MarshallingView">
            <constructor-arg ref="jaxbMarshaller" />
        </bean>
    
    
        <!-- JAXB2 marshaller. Automagically turns beans into xml -->
        <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
            <property name="classesToBeBound">
                <list>
                    <value>my.package.ResponseObj</value>
                </list>
            </property>
            <!-- Possibly include schema for validation -->
            <property name="schema" value="classpath:schema.xsd"/>
        </bean>

    Finally, the object we wish to expose (my.package.ResponseObj)
    Code:
    @XMLRootElement
    public class ResponseObj {
       private String something;
    
       public void setSomething(String s) {...}
       public String getSomething() {...}
    }

    That was the entire server section. Other than that, web.xml needs to be configuered just like any other app which uses annotated controllers.






    Then, the client side is just as slick. First the REST client imlementation:
    Code:
    public class restClient {
        RestTemplate restTemplate;
    
        public void fetchRESTObject() {
            ResponseObj obj = (ResponseObj) restTemplate.getForObject("http://url/myService/{param}", ResponseObj.class, "myParameterWord");
        }
    
        [...] 
    }

    Then the spring configuration to load the rest client
    Code:
        <!-- My REST client injected with spring RestTemplate -->
        <bean id="restClient" class="test.RestClient">
            <property name="restTemplate" ref="restTemplate"/>
        </bean>
    
    
        <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
            <property name="messageConverters">
                <list>
                    <bean id="messageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
                        <property name="marshaller" ref="xmlMarshaller" />
                        <property name="unmarshaller" ref="xmlMarshaller" />
                    </bean>
                </list>
            </property>
        </bean>
    
    
       <bean id="xmlMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" >
            <property name="classesToBeBound">
                <list>
                    <value>my.package.ResponseObj</value>
                </list>
            </property>
            <!-- Possibly use schema validation -->
            <property name="schema" value="schema.xsd" />
        </bean>


    Note how both endpoints use the same ResponseObj that I created. I simply copied the sources from the server to the client, although, any class having the same layout can act as a substitute.

    Client classes can even be generated from an XSD file. Note how maven and the xjc plugin can be used to be generate java to xsd on the server side, and then xsd to java on the client side. It is however doubtful that a REST-service should at all need an XSD file. The XML in itself and dokumentation should be enough.

    Lastly, note how the JAXB2 marshaller can be replaced by any other mashaller, such as XStream, Castor, XMLBeans etc.


    That was all, hope it was helpful
    Last edited by Toxic; Dec 23rd, 2009, 10:44 AM.

    Comment


    • #3
      Great

      Hi there

      thank you very much for such a great and in depth reply.

      I'll read over it and see how I get on!

      Thanks again

      Comment


      • #4
        Blog post on subject

        Hi there,

        Just to let you know I have compiled a quick blog post tutorial on this subject at:

        http://eggsylife.co.uk/2010/01/03/sp...-web-services/

        Example code can be downloaded at:

        http://code.google.com/p/eggsy-spring3-restful-example/

        (Toxic I have mentioned this you and this post on the blog post)

        Comment


        • #5
          Nice post. I'm glad it worked out for you



          As a minor side note; when using XML schemas to construct a contract-first RESTful service, it helps to solve some of the potential problems with JAXB2 by using property contextPath instead of classesToBeBound in the jaxbMarshaller on the client side.

          Comment


          • #6
            Example code can be downloaded at:

            http://code.google.com/p/eggsy-spring3-restful-example/

            I am not able to Check out the code from GoogleCode. Can you tell whether the SourceCode location is moved.

            URL Accessed to Download the Source :http://eggsy-spring3-restful-example...com/svn/trunk/ eggsy-spring3-restful-example-read-only

            Comment


            • #7
              SVN Checkout

              Hi there,

              The project is hosted on Google Code.

              I have just tested checking out the code through SVN and it successfully executed.

              Code:
              svn checkout http://eggsy-spring3-restful-example.googlecode.com/svn/trunk/ eggsy-spring3-restful-example-read-only

              Comment


              • #8
                Would you change anything with the introduction of Spring-ws 2.0?

                Thanks for the code snippet. I wonder if there's a clear advantage of using Spring-ws 2.0 for mixing RESTful and xml requests/responses?

                Comment


                • #9
                  Great stuff helped alot

                  Comment


                  • #10
                    Having a view problem

                    I used this example and I have a problem. here is the error stack :

                    javax.servlet.ServletException: Could not resolve view with name 'jaxbView' in servlet with name 'LabResult'
                    org.springframework.web.servlet.DispatcherServlet. render(DispatcherServlet.java:1029)
                    org.springframework.web.servlet.DispatcherServlet. doDispatch(DispatcherServlet.java:817)
                    org.springframework.web.servlet.DispatcherServlet. doService(DispatcherServlet.java:719)
                    org.springframework.web.servlet.FrameworkServlet.p rocessRequest(FrameworkServlet.java:644)
                    org.springframework.web.servlet.FrameworkServlet.d oGet(FrameworkServlet.java:549)
                    javax.servlet.http.HttpServlet.service(HttpServlet .java:690)
                    javax.servlet.http.HttpServlet.service(HttpServlet .java:803)

                    Comment


                    • #11
                      Originally posted by Itf View Post
                      I used this example and I have a problem. here is the error stack :

                      javax.servlet.ServletException: Could not resolve view with name 'jaxbView' in servlet with name 'LabResult'
                      org.springframework.web.servlet.DispatcherServlet. render(DispatcherServlet.java:1029)
                      org.springframework.web.servlet.DispatcherServlet. doDispatch(DispatcherServlet.java:817)
                      org.springframework.web.servlet.DispatcherServlet. doService(DispatcherServlet.java:719)
                      org.springframework.web.servlet.FrameworkServlet.p rocessRequest(FrameworkServlet.java:644)
                      org.springframework.web.servlet.FrameworkServlet.d oGet(FrameworkServlet.java:549)
                      javax.servlet.http.HttpServlet.service(HttpServlet .java:690)
                      javax.servlet.http.HttpServlet.service(HttpServlet .java:803)


                      You need two things:

                      1.

                      <!-- Resolve views based on string names -->
                      <bean class="org.springframework.web.servlet.view.BeanNa meViewResolver" />


                      2.

                      <!-- XML view using a JAXB2 marshaller -->
                      <bean id="jaxbView" class="org.springframework.web.servlet.view.xml.Ma rshallingView">
                      <constructor-arg ref="jaxbMarshaller" />
                      </bean>


                      And

                      Comment


                      • #12
                        I did all of that, here is my labresult-servlet.xml

                        <?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="http://www.springframework.org/schema/p"
                        xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
                        xsi:schemaLocation="http://www.springframework.org/schema/tx
                        http://www.springframework.org/schem...ing-tx-2.5.xsd
                        http://www.springframework.org/schema/beans
                        http://www.springframework.org/schem...ring-beans.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.xsd">


                        <bean
                        class="org.springframework.web.servlet.mvc.annotat ion.DefaultAnnotationHandlerMapping" />
                        <bean
                        class="org.springframework.web.servlet.mvc.annotat ion.AnnotationMethodHandlerAdapter" />

                        <!-- JAXB2 marshaller. Auto-magically turns beans into xml -->
                        <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshalle r">
                        <property name="classesToBeBound">
                        <list>
                        <value>org.msclinic.labresult.beans.ResultProfil es
                        </value>
                        <value>org.msclinic.labresult.beans.ResultProfil e
                        </value>
                        <value>org.msclinic.labresult.beans.Results</value>
                        </list>
                        </property>
                        </bean>
                        <bean id="labresult"
                        class="org.springframework.web.servlet.view.xml.Ma rshallingView">
                        <constructor-arg ref="jaxbMarshaller" />

                        </bean>
                        <bean
                        class="org.springframework.web.servlet.view.Conten tNegotiatingViewResolver">
                        <property name="mediaTypes">
                        <map>
                        <entry key="xml" value="application/xml" />
                        <entry key="html" value="text/html" />
                        </map>
                        </property>
                        <property name="viewResolvers">
                        <list>
                        <bean class="org.springframework.web.servlet.view.BeanNa meViewResolver" />
                        <bean class="org.springframework.web.servlet.view.UrlBas edViewResolver">
                        <property name="viewClass"
                        value="org.springframework.web.servlet.view.JstlVi ew" />
                        <property name="prefix" value="/WEB-INF/jsp/" />
                        <property name="suffix" value=".jsp" />

                        </bean>
                        </list>

                        </property>
                        </bean>

                        <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
                        destroy-method="close">
                        <property name="driverClassName" value="org.postgresql.Driver" />
                        <property name="url" value="jdbcostgresql://localhost:5432/labDB" />
                        <property name="username" value="sa" />
                        <property name="password" value="passwordPASSWORD" />
                        </bean>

                        <bean id="mySessionFactory"
                        class="org.springframework.orm.hibernate3.annotati on.AnnotationSessionFactoryBean">
                        <property name="dataSource" ref="myDataSource" />
                        <property name="annotatedClasses">
                        <list>
                        <value>org.msclinic.labresult.beans.ResultProfil e
                        </value>
                        </list>
                        </property>

                        <property name="hibernateProperties">
                        <props>
                        <prop key="hibernate.dialect"> org.hibernate.dialect.PostgreSQLDialect</prop>
                        <prop key="hibernate.show_sql">true</prop>
                        <prop key="hibernate.hbm2ddl.auto">create</prop>
                        </props>
                        </property>
                        </bean>

                        <bean id="myUserDAO"
                        class="org.msclinic.labresult.service.dao.impl.Res ultServiceDAOImpl">
                        <property name="sessionFactory" ref="mySessionFactory" />

                        </bean>

                        <bean name="labResults"
                        class="org.msclinic.labresult.web.controller.LabRe sultController">
                        <property name="userDAO" ref="myUserDAO" />
                        </bean>

                        <!-- ANNOTATION DRIVEN TRANSACTIONS -->
                        <bean id="txManager"
                        class="org.springframework.jdbc.datasource.DataSou rceTransactionManager">
                        <property name="dataSource" ref="myDataSource" />
                        </bean>
                        <tx:annotation-driven transaction-manager="txManager" />
                        </beans>

                        and web.xml:

                        <?xml version="1.0" encoding="UTF-8"?>
                        <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
                        <listener>
                        <listener-class>org.springframework.web.context.ContextLoade rListener</listener-class>
                        </listener>
                        <context-param>
                        <param-name>contextConfigLocation</param-name>
                        <param-value>
                        /WEB-INF/LabResult-servlet.xml
                        </param-value>
                        </context-param>

                        <servlet>
                        <servlet-name>LabResult</servlet-name>
                        <servlet-class>org.springframework.web.servlet.DispatcherSe rvlet</servlet-class>
                        <load-on-startup>1</load-on-startup>
                        </servlet>

                        <servlet-mapping>
                        <servlet-name>LabResult</servlet-name>
                        <url-pattern>*.list</url-pattern>
                        </servlet-mapping>
                        <welcome-file-list>
                        <welcome-file>index.jsp</welcome-file>
                        </welcome-file-list>
                        </web-app>

                        the index page get displayed as it doesn't pass through the servlet but all other pages gives the same error

                        Comment


                        • #13
                          Please use code tags when displaying code. Also, could it be that you only map ".list" operations in your web.xml?

                          Comment


                          • #14
                            Thanks Toxic,
                            I wanted to only map ".list". Should I map any thing else? in my controller, I only need ".list"
                            here is my controller:
                            Code:
                            @Controller
                            public class LabResultController {
                            
                            	private ResultServiceDAO resultServiceDAO;
                                  @Autowired
                            	public void setResultServiceDAO(ResultServiceDAO resultServiceDAO) {
                            		this.resultServiceDAO = resultServiceDAO;
                            	}
                                  @RequestMapping(method = RequestMethod.GET, value = "/labresult.list")
                            	public ModelAndView myRestMethod() {
                            
                            		result = resultServiceDAO.getResultProfileByResultId(0);
                            		if (!result.isEmpty()) {
                            
                            			return new ModelAndView("labresult", "response", result);
                            		} else
                            			return null;
                            	}
                            }

                            Comment


                            • #15
                              It seems that either your spring-config is not picked up properly or that your controller mappings are not. I'm not sure whether there's case-sensitivity involved in your servlet (e.g., try naming it lowercase "labresult"). In the latter case, you should also supply the beans

                              Code:
                              <context:component-scan base-package="my.controller.package"
                              <annotation-driven/>
                              Last edited by Toxic; Feb 24th, 2011, 11:38 AM.

                              Comment

                              Working...
                              X