RESTEasy integration with Spring – Tutorial Part 1 – Introduction

This is a step by step tutorial explaining how to integrate RESTEasy with Spring. The first part covers setting up simple REST Service, and request processing by Spring components.

Spring implementation is based on version 3 with dependency injection. I do not use Spring annotations, but javax.inject instead. I find this approach more flexible, since is not tightly coupled with particular technology.

RESTEasy can be deployed in a various ways – I will concentrate on the most popular one – which is the web application with component scan. At the begining we will start with web 2.4 and later on update to 3.0.

Stand alone deployment, without spring would consists of two main parts – servlet for request processing, bootstrap listener which handles configuration, and finally REST Services itself. Bootstrap listener scans classpath and looks for classes annotated with @Path – those are potential candidates for REST Service. They will be registered in request dispatcher in order to handle incoming HTTP calls. Since this is stand alone deployment, RESTEasy will simply create instances of found REST Services – this means, that those instances and not deployed in any particular container (except tomcat). For example they cannot access Spring context – especially because there is none. But if developer would like to access Spring context within such setup, he would need to manually load Spring context from REST Service class. This is typical approach when accessing Spring from servlet, but there is much better solution:

Let Spring manage services and their dependencies, and RESTEasy handles REST/HTTP related stuff.

In the stand alone example, the REST Services (annotated with @Path) were created by RESTEasy classpath scanner. Now we will use Spring to instantiate REST Services and all dependent components.
When the web application starts, as first step the SpringContextLoaderListener is being triggered – it initializes Spring context and all declared components.
In the second step, the RESTEasy bootstrap is being triggered – but this is special one, designed for usage with spring. It does not scan classpath for @Path annotated classes – instead it queries Spring for all beans with @Path annotation, and Spring returns references to those beans. This is a big difference, when compared to standalone setup. RESTEasy will not create new instance of REST Service, it will reference beans created by Spring. Basically this is the main difference between standalone deployment and one based on Spring – REST Service instances are created by Spring, so they are running within Spring container, and have access to all Spring components, like any other Spring bean. RESTEasy only forwards incoming calls to those Spring beans.

Hints:

  • Service, component, spring managed bean, REST Service – all are synonyms, and technically they are Plain Java Objects Objects managed by Spring
  • Do not instantiate services with new operator. This would create new class outside of Spring context, and such class cannot access Spring manged beans, and therefore injections will not work (will remain null).
  • Do not register REST servlet on root context (“/*”), always use some path (“/rest/*”). You might need to add another servlet in the future, and this new servlet will conflict with one registered on root context. You can remove extra path with proper Apache configuration.

Tutorial Application

This is a web application which exposes simple REST Service. Its implementation forwards incoming calls to Spring component, which handles business logic – in our case it is answer to traditional “Hello World” message.

REST Service exposes two methods:

  • GET – /Hello/text –  request/response attributes are passed as text in URL. Request has single msg path parameter.  Response contains message from request with appended “Hello” text.
  • POST – /Hello/javabean – request/response attributes are passed in body as JSON/XML documents. Request contains msg and additionally gender attribute. Response contains message from request with appended “Hello Sir” or “Hello Madam” – depending on provided gender. Response includes also server time, it’s not formatted – this will be covered later.

Curl examples:

Project Structure (Maven)

  • org.mmiklas.resttutorial.model – POJOs / transfer objects for REST Service
  • org.mmiklas.resttutorial.rest – REST Service (Resource) implementation
  • org.mmiklas.resttutorial.server – Spring component containing backed implementation
  • resources – XML configuration files for logger and Spring
  • webapp/WEB-INF – deployment descriptor

Spring Configuration

Spring configuration consists of single XML file which enables classpath component scan. Dependencies between Spring components are declared by annotations – @Named declares Spring component, and @Inject references existing component.

src/main/resources/tutorial-spring-context.xml

Deployment Descriptor

web.xml has following functions (later on we will update it to 3.0, not its 2.4):

  • initializes Spring context from XML file
  • initializes RESTEasy and enables Spring integration
  • registers RESTEasy dispatcher servlet on “/rest/*” context

src/main/webapp/WEB-INF/web.xml

Transfer Objects (POJOs)

Transfer objects will be serialized and deserialized from/to JSON. The configuration in both cases is made with JAXB annotations, new JSON parsers are supporting those – but make sure that you have latest version, because it was not always the case.

REST Service

REST implementation is based only on standard Java API – it does not have Spring or RESTEasy dependencies. This is really good approach to use standardized API as far as possible – it does not matter whenever you are planning to stick with particular framework forever – clean standardized code is easier to read.

@Named annotation defines HelloRestService class as Spring component, and @Path declares this class additionally as REST Service provider. Spring will create instance of our class, and RESTEasy will expose its methods over HTTP.

sayTextHello accepts GET messages on …./Hallo/text and reads query parameter msg.

sayJavaBeanHello accepts POST messages on …./Hallo/javabean and supports XML and JSON documents. XML is supported by default, since Java contains required providers (marshaller/unmarshaller). JSON must be enabled – since it’s not JDK part – I will describe that in next chapter.

Spring Component (Backend)

Spring implementation is self explanatory – service is declared with @Named. @Component would work as well, but it’s not standarized annotation.

JSON Provider

sayJavaBeanHello REST method supports JSON media type. In order to enable it, we only need to have proper jar in classpath:

RESTEasy scans during bootstrap phase classpath and searches for content providers – they are divided into two groups – marshaller and unmarshaller:

  • unmarshaller class in annotated with @Provider, @Consumes and implements MessageBodyReader interface
  • marshaller class in annotated with @Provider, @Produces and implements MessageBodyWriter interface

@Provider annotation is only marker declaring, that given class has some interesting functionality – first the implementing interface specifies it – annotation alone is not sufficient. @Produces and @Consumes are defining mime type which is supported by our provider – the same annotations can be found on rest methods.

Both interfaces above have generic type (T), which is currently set to Object – it provides possibility to register dedicated provider for the same mime type, but different class type. For example we can register different provider for User and Group and both for mime type JSON.

Project Source Download

resteasy_spring_p1_src.zip

Advertisements

9 thoughts on “RESTEasy integration with Spring – Tutorial Part 1 – Introduction

  1. Vivek

    Problem: I was getting the ClassNotFoundException: org.jboss.resteasy.plugins.spring.SpringContextLoaderListener

    WARN:oejw.StandardDescriptorProcessor:Could not instantiate listener org.jboss.resteasy.plugins.spring.SpringContextLoaderListener
    java.lang.ClassNotFoundException: org.jboss.resteasy.plugins.spring.SpringContextLoaderListener
    at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:244)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:230)

    SOLN: Fixed it by including the Dependency

    org.jboss.resteasy
    resteasy-spring
    2.3.3.Final

    Reply
  2. Asif Garhi

    In the example above HelloRestService is a class. I did the same and it works wonders. When I change that to an interface and implement it – REST container cannot find the resource and I get “Could not find resource ..” error.
    What do you need to make this work with Interfaces instead of Class?
    Thanks for a wonderful tutorial.

    Reply
      1. Asif Garhi

        Thanks for your reply. I have not done any of this. Can you please provide more details or point me to an online tutorial for that.

        Thanks again.

  3. http://www.waagg.com/blog/969283/significance-of-a-construction-safety-checklist/

    Your way of explaining the whole thing in this
    paragraph is truly good, all be able to without difficulty know it, Thanks a lot.

    Reply
  4. Basha

    Hi,

    Below I am getting error, while running the application, it looks like required jars are not there. Would you please share me the source code with jar files as well.

    My E-mailid: mohdbasha.y@gmail.com

    INFO: Initializing Spring root WebApplicationContext
    2013-09-27 20:01:43,852 | main | | INFO | org.springframework.web.context.ContextLoader:272 | Root WebApplicationContext: initialization started
    2013-09-27 20:01:43,915 | main | | INFO | org.springframework.web.context.support.XmlWebApplicationContext:447 | Refreshing Root WebApplicationContext: startup date [Fri Sep 27 20:01:43 IST 2013]; root of context hierarchy
    2013-09-27 20:01:44,021 | main | | ERROR | org.springframework.web.context.ContextLoader:312 | Context initialization failed
    java.lang.NoSuchMethodError: org.springframework.web.context.support.XmlWebApplicationContext.getEnvironment()Lorg/springframework/core/env/ConfigurableEnvironment;
    at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:87)
    at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
    at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:458)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:388)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:384)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
    at org.jboss.resteasy.plugins.spring.SpringContextLoaderListener.contextInitialized(SpringContextLoaderListener.java:44)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4206)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4705)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
    at org.apache.catalina.core.StandardService.start(StandardService.java:525)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:754)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
    Sep 27, 2013 8:01:44 PM org.apache.catalina.core.StandardContext listenerStart
    SEVERE: Exception sending context initialized event to listener instance of class org.jboss.resteasy.plugins.spring.SpringContextLoaderListener
    java.lang.NoSuchMethodError: org.springframework.web.context.support.XmlWebApplicationContext.getEnvironment()Lorg/springframework/core/env/ConfigurableEnvironment;
    at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:87)
    at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
    at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:458)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:388)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:384)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
    at org.jboss.resteasy.plugins.spring.SpringContextLoaderListener.contextInitialized(SpringContextLoaderListener.java:44)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4206)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4705)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
    at org.apache.catalina.core.StandardService.start(StandardService.java:525)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:754)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
    Sep 27, 2013 8:01:44 PM org.apache.catalina.core.StandardContext start
    SEVERE: Error listenerStart
    Sep 27, 2013 8:01:44 PM org.apache.catalina.core.StandardContext start
    SEVERE: Context [/resteasy_spring_p1] startup failed due to previous errors
    Sep 27, 2013 8:01:44 PM org.apache.catalina.core.ApplicationContext log
    INFO: Closing Spring root WebApplicationContext
    2013-09-27 20:01:44,026 | main | | INFO | org.springframework.web.context.support.XmlWebApplicationContext:938 | Closing Root WebApplicationContext: startup date [Fri Sep 27 20:01:43 IST 2013]; root of context hierarchy
    2013-09-27 20:01:44,026 | main | | ERROR | org.springframework.web.context.support.XmlWebApplicationContext:946 | Exception thrown from ApplicationListener handling ContextClosedEvent
    java.lang.IllegalStateException: ApplicationEventMulticaster not initialized – call ‘refresh’ before multicasting events via the context: Root WebApplicationContext: startup date [Fri Sep 27 20:01:43 IST 2013]; root of context hierarchy
    at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:307)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:294)
    at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:943)
    at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:917)
    at org.springframework.web.context.ContextLoader.closeWebApplicationContext(ContextLoader.java:541)
    at org.springframework.web.context.ContextLoaderListener.contextDestroyed(ContextLoaderListener.java:142)
    at org.apache.catalina.core.StandardContext.listenerStop(StandardContext.java:4245)
    at org.apache.catalina.core.StandardContext.stop(StandardContext.java:4886)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4750)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
    at org.apache.catalina.core.StandardService.start(StandardService.java:525)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:754)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
    Sep 27, 2013 8:01:44 PM org.apache.catalina.core.StandardContext listenerStop
    SEVERE: Exception sending context destroyed event to listener instance of class org.jboss.resteasy.plugins.spring.SpringContextLoaderListener
    java.lang.IllegalStateException: LifecycleProcessor not initialized – call ‘refresh’ before invoking lifecycle methods via the context: Root WebApplicationContext: startup date [Fri Sep 27 20:01:43 IST 2013]; root of context hierarchy
    at org.springframework.context.support.AbstractApplicationContext.getLifecycleProcessor(AbstractApplicationContext.java:320)
    at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:950)
    at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:917)
    at org.springframework.web.context.ContextLoader.closeWebApplicationContext(ContextLoader.java:541)
    at org.springframework.web.context.ContextLoaderListener.contextDestroyed(ContextLoaderListener.java:142)
    at org.apache.catalina.core.StandardContext.listenerStop(StandardContext.java:4245)
    at org.apache.catalina.core.StandardContext.stop(StandardContext.java:4886)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4750)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
    at org.apache.catalina.core.StandardService.start(StandardService.java:525)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:754)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
    Sep 27, 2013 8:01:44 PM org.apache.coyote.http11.Http11Protocol start
    INFO: Starting Coyote HTTP/1.1 on http-8080
    Sep 27, 2013 8:01:44 PM org.apache.jk.common.ChannelSocket init
    INFO: JK: ajp13 listening on /0.0.0.0:8009
    Sep 27, 2013 8:01:44 PM org.apache.jk.server.JkMain start
    INFO: Jk running ID=0 time=0/17 config=null
    Sep 27, 2013 8:01:44 PM org.apache.catalina.startup.Catalina start
    INFO: Server startup in 2234 ms

    Thanks,
    Basha

    Reply
  5. SEO Fort Lauderdale FL

    Wonderful goods from you, man. I have take into account your stuff previous to and you’re just extremely excellent.
    I really likje what you hhave got right here, certainly like
    what you’re sating and the way wherein you are saying
    it. You make it enjoygable and you continue to care for to keep it
    smart. I can’t wait to learn much more from you.
    This is actually a terrific website.

    Reply

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