Restlet with Spring

My first post is about to write a very simple authenticated rest server with :

An easy way to try this sample rest servlet is with jetty (http://www.mortbay.org/jetty/)
and the maven-jetty-plugin

We can use curl to make a rest request (http://blogs.plexibus.com/2009/01/15/rest-esting-with-curl/)

  • First, create the sample project structure with maven

    mvn archetype:generate -DgroupId=com.jgoday -DartifactId=restservlet -DarchetypeArtifactId=maven-archetype-webapp

    maven will create the following structure :

    .
    ./src
    ./src/main
    ./src/main/resources
    ./src/main/webapp
    ./src/main/webapp/WEB-INF
    ./src/main/webapp/WEB-INF/web.xml
    ./src/main/webapp/index.jsp
    ./pom.xml

  • Edit pom.xml to add the project dependencies and restlet repository:
    • Spring and spring-security
      
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring</artifactId>
        <version>2.5.5</version>
      </dependency>
      <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core</artifactId>
        <version>2.0.4</version>
      </dependency>
      
    • Restlet
      
      <dependency>
        <groupId>org.restlet</groupId>
        <artifactId>org.restlet</artifactId>
        <version>1.1.3</version>
      </dependency>
      <dependency>
        <groupId>org.restlet</groupId>
        <artifactId>org.restlet.ext.spring</artifactId>
        <version>1.1.3</version>
      </dependency>
      <dependency>
        <groupId>com.noelios.restlet</groupId>
        <artifactId>com.noelios.restlet.ext.servlet</artifactId>
        <version>1.1.3</version>
      </dependency>
      <dependency>
        <groupId>com.noelios.restlet</groupId>
        <artifactId>com.noelios.restlet.ext.spring</artifactId>
        <version>1.1.3</version>
      </dependency>
      

      We have to add the restlet repository (http://www.restlet.org/downloads/maven)

       
      <repository>
        <id>maven-restlet</id>
        <name>Public online Restlet repository</name>
        <url>http://maven.restlet.org</url>
      </repository>
      
    • Jetty
      cglib dependency for jetty

      
      <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2</version>
      </dependency>
      

      And configure the jetty plugin in the build section

      
      <plugin>
          <groupId>org.mortbay.jetty</groupId>
          <artifactId>maven-jetty-plugin</artifactId>
          <version>6.1.10</version>
      </plugin>
      
  • We are going to create a sample REST resource (SampleResource)
    that will receive an user name and will return a ‘hello world’ xml representation

    First we have to describe the rest servlet in the web.xml file

    
    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    <web-app>
      <display-name>Sample rest servlet</display-name>
    
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
          /WEB-INF/applicationContext.xml
        </param-value>
      </context-param>
    
      <servlet>
        <servlet-name>rest</servlet-name>
        <servlet-class>
          com.noelios.restlet.ext.spring.RestletFrameworkServlet
        </servlet-class>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>rest</servlet-name>
        <url-pattern>/rest/*</url-pattern>
      </servlet-mapping>
    
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    </web-app>
    
  • We define a rest servlet called ‘rest’ , so we need a rest-servlet.xml file to define the resources
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    
        <bean id="root" class="org.restlet.ext.spring.SpringRouter">
            <property name="attachments">
                <map>
                    <entry key="/sample/{name}">
                        <bean class="org.restlet.ext.spring.SpringFinder">
                            <lookup-method name="createResource" bean="sampleResource" />
                        </bean>
                    </entry>
                </map>
            </property>
        </bean>
    
        <bean id="sampleResource" class="com.jgoday.rest.resources.SampleResource" scope="prototype" />
    </beans>
    
  • And now, define the sampleResource code that will return a xml with the output message
    
    package com.jgoday.rest.resources;
    
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    
    import org.restlet.data.MediaType;
    import org.restlet.resource.DomRepresentation;
    import org.restlet.resource.Representation;
    import org.restlet.resource.Resource;
    import org.restlet.resource.Variant;
    
    public class SampleResource extends Resource
    {
        public SampleResource ()
        {
            this.getVariants ().add (new Variant (MediaType.TEXT_XML));
        }
    
        public Representation represent (Variant variant)
        {
            Representation resource = null;
    
            try {
                resource = new DomRepresentation (MediaType.TEXT_XML);
                Document doc = ((DomRepresentation) resource).getDocument ();
    
                Element root = doc.createElement ("message");
                root.setTextContent ("Hello world " +
                    this.getRequest ().getAttributes ().get ("name") + “!”);
    
                doc.appendChild (root);
                doc.normalizeDocument ();
            }
            catch (Exception e) {
                e.printStackTrace ();
            }
    
            return resource;
        }
    }
    
  • We can try it now with jetty and curl (to make the rest request):

    mvn package && mvn jetty:run
    curl -i -H "Accept: text/xml" -X GET http://localhost:8080/restservlet/rest/sample/tommy

    and we get

    
    <?xml version="1.0" encoding="UTF-8"><message>Hello world tommy!</message>
    
Now it’s time to add the security stuff

  • Add the spring security filter in web.xml
    
    <filter>
      <filter-name>springSecurityFilterChain</filter-name>
      <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    
    <filter-mapping>
      <filter-name>springSecurityFilterChain</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>
    
  • configure spring-security (in applicationContext.xml or other spring definition file)
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:security="http://www.springframework.org/schema/security"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
    		      http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    		      http://www.springframework.org/schema/security
    		      http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
      <security:http>
        <security:http-basic />
        <security:form-login />
        <security:intercept-url pattern="/**" access="ROLE_USER"  />
      </security:http>
    
      <security:authentication-provider>
        <security:password-encoder hash="md5" />
        <security:user-service>
          <security:user name="user"
    		     password="baa7a52965b99778f38ef37f235e9053"
                         authorities="ROLE_USER" />
        </security:user-service>
      </security:authentication-provider>
    </beans>
    

we add security to all site, both http-basic and form authentication methods

And it’s done !

If we try to access with a browser, spring-security will automatically show a auth form:
spring auth form

And if we try again with curl, we should use an username and a password:


curl -i -H "Accept: text/xml" -X GET http://localhost:8080/restservlet/rest/sample/tommy -u user

curl auth

In the resource, if we want to get the user information, we can use SecurityContext
http://static.springsource.org/spring-security/site/apidocs/index.html


import org.springframework.security.context.SecurityContext;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.userdetails.User;

SecurityContext context = SecurityContextHolder.getContext ();
User user = (User) context.getAuthentication ().getPrincipal ();

Element userElement = doc.createElement ("user_info");
userElement.setTextContent (user.getUsername ());

Advertisements
  1. Thanks for your article. It is exactly what i am looking for.
    One question. How you handle security exception? i notice that it comes bad with html code? How can i make it xml?
    Apart from that, i see other companies actually gives away api key instead. How you would handle that?

    thanks

      • jgoday
      • May 29th, 2009

      Hi, spring-security authomatically handles the exception returning the http error code and the message.
      For example, with QT, if the auth data is wrong we get
      the QNetworkReply::AuthenticationRequiredError and the message ‘server replied: Bad credentials’

      part from that, i see other companies actually gives away api key instead. How you would handle that?

      Do you mean ‘how to handle it with restlet ?’

  2. Hi,nice article. I m getting following exception while running it. I m using tomcat 6.0. Kindly help me in this regard

    java.lang.NoSuchMethodError: com.noelios.restlet.http.HttpServerCall.(Ljava/lang/String;I)V
    at com.noelios.restlet.ext.servlet.ServletCall.(ServletCall.java:110)
    at com.noelios.restlet.ext.servlet.ServletConverter.service(ServletConverter.java:185)
    at com.noelios.restlet.ext.spring.RestletFrameworkServlet.doService(RestletFrameworkServlet.java:106)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:523)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:453)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:378)
    at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
    at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.SessionFixationProtectionFilter.doFilterHttp(SessionFixationProtectionFilter.java:67)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.basicauth.BasicProcessingFilter.doFilterHttp(BasicProcessingFilter.java:174)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter.doFilterHttp(DefaultLoginPageGeneratingFilter.java:86)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:277)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:183)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:138)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
    at java.lang.Thread.run(Thread.java:619)

  3. I have solved my issue .Thanks a lot

      • jay
      • November 7th, 2011

      How did u solve this? i’am getting the same issue with servicemix and camel.

  4. Hi,

    I have gone through the example you provided on your website. and have a quick question. You have defined following code in your web.xml and also another file called rest-servlet.xml which again seems to be the spring beans file. What is the difference between them ?

    contextConfigLocation

    /WEB-INF/applicationContext.xml

    Also suppose in your resource you need another spring defined service how will you do that. and what all entries will you make in you respective xml files.

    Any help on this is highly appreciated.

    Thanks,
    Rahul

      • jgoday
      • November 16th, 2009

      Unless you tell it specified, the RestletFrameworkServlet looks for a ‘Servlet name’.xml file to configure the rest resources.
      If i understand you well, you always can define more spring files,
      /WEB-INF/applicationContext-*.xml for example (applicationContext-dao.xml, applicationContext-services.xml …)

  5. Hi,

    Thanks a lot for providing this example. It helped me a lot. I was stuck using Restlet with Spring and your article helped. Thanks again.

  6. This was an extremely helpful blog. I appreciate you taking the time. I learned how to combine Restlets and Spring

  7. The tutorial is good. Just one correction:
    With configuration you outlined here the session will be created on the server side and associated with the client using cookie.
    When you implement REST services keep in mind that it’s stateless services(in most of the cases) and auth information should be passed on each request.
    To achieve it with spring security configuration you can add create-session=”never” attribute and replace http-form with http-basic authentication:

    • magic mesh
    • January 5th, 2013

    I was suggested this website by my cousin. I’m not sure whether this post is written by him as no one else know such detailed about my problem. You’re wonderful!
    Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: