A Technology Blog About Code Development, Architecture, Operating System, Hardware, Tips and Tutorials for Developers.

Showing posts with label MAVEN. Show all posts
Showing posts with label MAVEN. Show all posts

Wednesday, October 5, 2016

Custom Maven Plugin

7:30:00 PM Posted by Satish , No comments
When you write a custom plugin, you are going to be writing a series of Mojos (goals). Every Mojo is a single Java class which contains a series of annotations that tell Maven how to generate the Plugin descriptor. Before you can start writing Mojo classes, you will need to create Maven project with the appropriate packaging and POM.

To create a plugin project, you should use the Maven Archetype plugin. The following command-line will create a plugin with a groupId of org.tat.api and the artifactId of webapp-optimizer:

pom.xml
The most important element in a plugin project’s POM is the packaging element which has a value of maven-plugin. The other important piece of a plugin project’s POM is the dependency on the Maven Plugin API. This project depends on version 2.0 of the maven-plugin-api and it also adds in JUnit as a test-scoped dependency.

The Mojo implementation shown in the following example implements the Mojo interface by extending the org.apache.maven.plugin.AbstractMojo class. Before we dive into the code for this Mojo, let’s take some time to explore the methods on the Mojo interface. Mojo provides the following methods:

void setLog( org.apache.maven.monitor.logging.Log log )
When Maven loads and executes a Mojo, it is going to call the setLog() method and supply the Mojo instance with a suitable logging destination to be used in your custom plugin.

protected Log getLog()
Maven is going to call setLog() before your Mojo is executed, and your Mojo can retrieve the logging object by calling getLog().

void execute() throws org.apache.maven.plugin.MojoExecutionException
This method is called by Maven when it is time to execute your goal.


You can run the plug-in using the following command. The commands is as per the groupId:artifactId:version:goal format.

 When you run this command-line you should see output that contains the output of the echo goal with the default message: "Hello Maven World…". If you want to customize the message, you can pass the value of the message parameter with the following command-line:

Saturday, December 15, 2012

RESTful Web Service with Spring 3.1

8:22:00 PM Posted by Satish , , , , , , , , , 9 comments
I posted lot of tutorials to create RESTful web services using different APIs like JAX-RS With GlassFish Jersey, JAX-RS With Jboss RESTEasy and JAX-RS With Apache CXF. I also posted the tutorials for Spring integration in Spring And Jersey, Spring and RESTEasy and Spring And CXF. But when it comes to RESTful web services, I personally like to use Spring 3.1 MVC. Some time back when RESTful web services were booming in industry, SpringSource developers released Spring 3.1 with REST support. They used the existing Spring MVC request mapping and took the help of JAX-RS 2.0 implementation for JSON and XML marshaling/ demarshaling. That is how they made it very simple.


This tutorial, I am going to demonstrate how to build a RESTful web service using Spring 3.1 MVC. Not only I will be creating a demo, along with that I will be demonstrating the CRUD operation with one of the model object. In this case I will be using FooBar as model object.

I am going to use the following tools and technologies in this turorial.
  • Spring 3.1.0.RELEASE
  • jackson-mapper-asl 1.9.9
  • jaxb-api 2.2.7
  • JDK 1.7
  • Tomcat 7.0
  • Maven
  • Eclipse 
I am going to take you to the following areas in this tutorial.
  • Service/Controller
  • Model Class
  • Spring MVC Context Configuration
  • Spring Integration With Web Container
  • Application Deployment
  • Testing
  • Source Code
Before start coding let's create a dynamic web project using the following maven command.

1
mvn archetype:generate -DgroupId=com.techiekernel.rest -DartifactId=SpringMVC-REST -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

After executing, the project will be get created with pom.xml file. As I am using JDK 7 and annotations, I have to specify the updated maven plugin. After giving the Jackson, JAXB and Spring dependency, the pom.xml looks as following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.techiekernel.rest</groupId>
  <artifactId>SpringMVC-REST</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>SpringMVC-REST Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <properties>
    <jackson-mapper-asl.version>1.9.9</jackson-mapper-asl.version>
    <jaxb-api.version>2.2.7</jaxb-api.version>
  </properties>

  <dependencies>

    <!-- Spring 3 dependencies -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>3.1.0.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.1.0.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>3.1.0.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>3.1.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.codehaus.jackson</groupId>
      <artifactId>jackson-mapper-asl</artifactId>
      <version>${jackson-mapper-asl.version}</version>
    </dependency>
    <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>${jaxb-api.version}</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1.1</version>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
    <finalName>SpringMVC-REST</finalName>
  </build>
</project>

Service/Controller:

In this case, we are going to write the web service in our Spring MVC controller. As you can see I have created POST, PUT, GET, DELETE methods to demonstrate the CRUD operations on FooBar. All the other configurations  are well self explanatory.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package com.techiekernel.service;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.techiekernel.model.FooBar;

@Controller
@RequestMapping("/foobar")
public class FooBarService {

  static Set<FooBar> fooBars;

  static {
    fooBars = new HashSet<FooBar>();
    FooBar foobar = null;
    for (int i = 0; i < 10; i++) {
      foobar = new FooBar(i, "Techie Kernel " + i);
      fooBars.add(foobar);
    }
  }

  @RequestMapping(value = "/{foobarId}", method = RequestMethod.GET, headers = "Accept=application/xml, application/json", produces = {
      "application/json", "application/xml" })
  @ResponseBody
  public FooBar getFoobar(@PathVariable int foobarId) {
    for (FooBar foobar : fooBars) {
      if (foobar.getId() == foobarId)
        return foobar;
    }
    return null;
  }

  @RequestMapping(method = RequestMethod.GET, headers = "Accept=application/xml, application/json", produces = {
      "application/json", "application/xml" })
  @ResponseBody
  public Set<FooBar> getFoobars() {
    return fooBars;
  }

  @RequestMapping(value = "/{foobarId}", method = RequestMethod.PUT, headers = "Accept=application/xml, application/json", produces = {
      "application/json", "application/xml" }, consumes = {
      "application/json", "application/xml" })
  @ResponseBody
  public FooBar editFoobar(@RequestBody FooBar foobar,
      @PathVariable int foobarId) {
    for (FooBar foobar1 : fooBars) {
      if (foobarId == foobar1.getId()) {
        foobar1.setId(foobar.getId());
        foobar1.setName(foobar.getName());
        return foobar1;
      }
    }
    return null;
  }

  @RequestMapping(value = "/{foobarId}", method = RequestMethod.DELETE, headers = "Accept=application/xml, application/json", produces = {
      "application/json", "application/xml" })
  @ResponseBody
  public boolean deleteFoobar(@PathVariable int foobarId) {
    System.out.println("Delete call.");
    Iterator<FooBar> fooIterator = fooBars.iterator();
    while (fooIterator.hasNext()) {
      FooBar foobar = fooIterator.next();
      System.out.println(foobar);
      if (foobar.getId() == foobarId) {
        fooIterator.remove();
        return true;
      }
    }
    return false;
  }

  @RequestMapping(method = RequestMethod.POST, headers = "Accept=application/xml, application/json", produces = {
      "application/json", "application/xml" }, consumes = {
      "application/json", "application/xml" })
  @ResponseBody
  public boolean createFoobar(@RequestBody FooBar fooBar) {
    return fooBars.add(fooBar);
  }

}

Model Class:

This is the FooBar our model class which has to be marshaled and demarshaled by JAX-RS APIs. So be very care full to annotate @XmlRootElement and @JsonAutoDetect. These two annotations will mark the class for marshaling and marshaling.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.techiekernel.model;

import javax.xml.bind.annotation.XmlRootElement;

import org.codehaus.jackson.annotate.JsonAutoDetect;

@XmlRootElement
@JsonAutoDetect
public class FooBar {
  int id;
  String name;

  public FooBar() {
    this.id = 1;
    this.name = "Techie Kernel";

  }
  
  public FooBar(int id, String name) {
    this.id = id;
    this.name = name;

  }

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return "FooBar [id=" + id + ", name=" + name + ", hashCode()="
        + hashCode() + "]";
  }
  
  @Override
  public int hashCode() {
    // TODO Auto-generated method stub
    return this.id;
  }
  
  @Override
  public boolean equals(Object obj) {
    if (obj instanceof FooBar) {
      if(this.id == ((FooBar)obj).id)
        return true;
    }
    return false;
  }
  
}

Spring MVC Context Configuration:

Now it's time to save the Spring MVC configuration in a xml file. Let's save the mvc-dispacher-servlet.xml in WEB-INF folder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:mvc="http://www.springframework.org/schema/mvc" 
  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-3.1.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
  <mvc:annotation-driven />
  <context:component-scan base-package="com.techiekernel.service" />
</beans>

Spring Integration With Web Container:

The integration is going to be in web.xml. Register Spring “ContextLoaderListener” listener class and specify the Spring MVC dispacher servlet “org.springframework.web.servlet.DispatcherServlet“ as front controller for all requests.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<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>Spring MVC REST</display-name>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>
                    org.springframework.web.servlet.DispatcherServlet
                </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
 
  <servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

Application Deployment:

Now every thing is ready and time to build for creating a war file.

1
mvn clean install;

On successful completion of the above command a war file will be get created in target folder. Copy the war file to webapp folder of your tomcat and start the tomcat. For you people to test, I have put the application in the VMWare Cloud. You can access the application using this URL.

Testing:

I could have wrote a Junit test to test all the services. But for a better user experience, I would suggest you to test using one of the REST tools with browser. I am using REST Client addon from Mozzila Firefox. As I have told I am going to demonstrate the CRUD operations on model object FooBar, I will be doing the all four GET, POST, PUT and DELETE operations using the Cloud URL. I would request you not to hit the URLs blindly, as at some time there there will be a change in data or there will be no data, so better hit the GET URL first to see the data status. I will be using UNIX curl command here to give you a idea for testing. 

http://localhost:8080/SpringMVC-REST - localhost tomcat URL
http://springmvc-rest.cloudfoundry.com - Cloud URL


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Get:
curl -i http://springmvc-rest.cloudfoundry.com/foobar -H "accept:application/json" -X GET
curl -i http://springmvc-rest.cloudfoundry.com/foobar/1 -H "accept:application/json" -X GET
curl -i http://springmvc-rest.cloudfoundry.com/foobar/1 -H "accept:application/xml" -X GET

PUT:
curl -i http://springmvc-rest.cloudfoundry.com/foobar/1 -H "accept:application/json" -H "content-Type:application/json" -X PUT -d "{  \"id\": 1,  \"name\": \"Techie Kernel-modified\"}"

POST:
curl -i http://springmvc-rest.cloudfoundry.com/foobar -H "accept:application/json" -H "content-Type:application/json" -X POST -d "{  \"id\": 10,  \"name\": \"Techie Kernel 10\"}"

DELETE:
curl -i http://springmvc-rest.cloudfoundry.com/foobar/10 -H "accept:application/json" -X DELETE


Source Code:

You can pull the code from GitHub.

Friday, December 14, 2012

Spring And CXF

8:38:00 PM Posted by Satish , , , , , , , , , No comments
This tutorial I am going to demonstrate how to integrate Apache CXF and Spring 3 to create a RESTFul web service. Using this I am going to show how to inject a bean to the web service class. In this example I will be injecting the service layer object to web service.
I am going to use the following tools and technologies.
  • CXF 2.5.0
  • Spring 3.0.5.RELEASE
  • JDK 1.7
  • Tomcat 7.0
  • Maven
  • Eclipse 
I am going to take you to the following areas in this tutorial.
  • Service Layer Interface
  • Service Layer Class
  • Web Service Class
  • Spring Bean Configuration
  • Configuration for Spring and Apache CXF
  • Application Deployment
  • Testing
Before start coding let's create a dynamic web project using the following maven command. 

1
mvn archetype:generate -DgroupId=com.techiekernel.rest -DartifactId=Spring-CXF -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

After executing, the project will be get created with pom.xml file. As I am using JDK 7 and annotations, I have to specify the updated maven plugin. After giving the CXF and Spring dependency, the pom.xml looks as following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.techiekernel.rest</groupId>
  <artifactId>Spring-CXF</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>JAXRS-CXF Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <spring.version>3.0.5.RELEASE</spring.version>
    <cxf.version>2.5.0</cxf.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxws</artifactId>
      <version>${cxf.version}</version>
    </dependency>

    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxrs</artifactId>
      <version>${cxf.version}</version>
    </dependency>

    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-transports-http</artifactId>
      <version>${cxf.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-oxm</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1.1</version>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
    <finalName>Spring-CXF</finalName>
  </build>
</project>

Service Layer Interface:

As I will show the Spring IOC or dependency injection, I am going to create the service layer interface and later a implimentation for the same.

1
2
3
4
5
package com.techiekernel.service;

public interface FooBarService {
  public String getMessage(String msg);
}

Service Layer Class:

1
2
3
4
5
6
7
8
package com.techiekernel.service;

public class FooBarServiceImpl implements FooBarService{
  public String getMessage(String msg) {
    String output = "FooBar say : " + msg;
    return output;
  }
}

Web Service Class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.techiekernel.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.techiekernel.service.FooBarService;

@Component
public class FooBarWS {
  //@Autowired
  FooBarService fooBarService;
 
  @GET
  @Path("/{param}")
  public Response getMessage(@PathParam("param") String msg) {
    return Response.status(200).entity(fooBarService.getMessage(msg)).build();
  }

  public FooBarService getFooBarService() {
    return fooBarService;
  }

  public void setFooBarService(FooBarService fooBarService) {
    this.fooBarService = fooBarService;
  }
  
}

Spring Bean Configuration:

Now it is time to cleate the configuration for spring beans in applicationContext.xml. It is very much required to place the following configuration in classpath, so that container will read from the deployment descriptor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?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:jaxrs="http://cxf.apache.org/jaxrs"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  <bean id="FooBarService" class="com.techiekernel.service.FooBarServiceImpl" />

  <bean id="fooBarWsClass" class="com.techiekernel.rest.FooBarWS">
    <property name="fooBarService" ref="FooBarService"></property>
  </bean>
  <jaxrs:server id="fooBarWs" address="/foobar">
    <jaxrs:serviceBeans>
      <ref bean="fooBarWsClass" />
    </jaxrs:serviceBeans>
  </jaxrs:server>
</beans>

Configuration for Spring and CXF:

The integration is going to be in web.xml. Register Spring “ContextLoaderListener” listener class and specify the CXF servlet “org.apache.cxf.transport.servlet.CXFServlet“ as front controller for web services.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<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>Spring CXF</display-name>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
    <listener>
  <listener-class>
                        org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <servlet>
    <servlet-name>CXFServlet</servlet-name>
    <display-name>CXF Servlet</display-name>
    <servlet-class>
      org.apache.cxf.transport.servlet.CXFServlet
    </servlet-class>
    <load-on-startup>2</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/ws/*</url-pattern>
  </servlet-mapping>
 
</web-app>

Application Deployment:

Now every thing is ready and time to build for creating a war file.

1
mvn clean install;

On successful completion of the above command a war file will be get created in target folder. Copy the war file to webapp folder of your tomcat and start the tomcat.

Testing:

Once tomcat is started use the following url to access the web service.

1
http://localhost:8080/Spring-CXF/ws/foobar/techiekernel

Output:

Once you hit the url in the browser, you are going to get the following output.


1
FooBar say : techiekernel


Source Code:

You can pull the code from GitHub.

Thursday, December 13, 2012

Spring and RESTEasy

9:09:00 PM Posted by Satish , , , , , , , , , No comments
This tutorial I am going to demonstrate how to integrate Jboss RESTEasy and Spring 3 to create a RESTFul web service. Using this I am going to show how use spring context to get the service bean in web service class.
I am going to use the following tools and technologies.
  • Jersey 2.2.1.GA
  • Spring 3.0.5.RELEASE
  • JDK 1.7
  • Tomcat 7.0
  • Maven
  • Eclipse 
I am going to take you to the following areas in this tutorial.
  • Service Layer Interface
  • Service Layer Class
  • Web Service Class
  • Spring Bean Configuration
  • Configuration for Spring and RESTEasy
  • Application Deployment
  • Testing
Before start coding let's create a dynamic web project using the following maven command. 

1
mvn archetype:generate -DgroupId=com.techiekernel.rest -DartifactId=Spring-RESTEasy -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

After executing, the project will be get created with pom.xml file. As I am using JDK 7 and annotations, I have to specify the updated maven plugin. After giving the RESTEasy and Spring dependency, the pom.xml looks as following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.techiekernel.rest</groupId>
  <artifactId>Spring-RESTEasy</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>JAXRS-Jersey Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <repositories>
    <repository>
      <id>JBoss repository</id>
      <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
    </repository>
  </repositories>
 
  <dependencies>
 
    <!-- JBoss RESTEasy -->
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jaxrs</artifactId>
      <version>2.2.1.GA</version>
    </dependency>
 
    <!-- Spring 3 dependencies -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>3.0.5.RELEASE</version>
    </dependency>
 
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.0.5.RELEASE</version>
    </dependency>
 
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>3.0.5.RELEASE</version>
    </dependency>
 
    <!-- need for solution 1, if your container don't have this -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
    </dependency>
    </dependencies>
    <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1.1</version>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
    <finalName>Spring-RESTEasy</finalName>
  </build>
</project>

Service Layer Interface:

1
2
3
4
5
package com.techiekernel.service;

public interface FooBarService {
  public String getMessage(String msg);
}

Service Layer Class:

1
2
3
4
5
6
7
8
package com.techiekernel.service;

public class FooBarServiceImpl implements FooBarService{
  public String getMessage(String msg) {
    String output = "FooBar say : " + msg;
    return output;
  }
}

Web Service Class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.techiekernel.rest;

import javax.servlet.ServletContext;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.techiekernel.service.FooBarService;

@Component
@Path("/foobar")
public class FooBarWS {
  FooBarService fooBarService;
 
  @GET
  @Path("/{param}")
  public Response getMessage(@PathParam("param") String msg, @Context ServletContext servletContext) {
    //get Spring application context
        ApplicationContext ctx = 
                         WebApplicationContextUtils.getWebApplicationContext(servletContext);
        fooBarService= ctx.getBean("FooBar",FooBarService.class);
    return Response.status(200).entity(fooBarService.getMessage(msg)).build();
  }
}

Using the spring context to get the bean in stead of IOC does not look good to me. I suggest you to check this post for better integration. I followed the example, but that did not work for me.. So I end up doing this mess. But I strongly suggest to give a try to this post.

Spring Bean Configuration:

Now it is time to create the configuration for spring beans in applicationContext.xml. It is very much required to place the following configuration in classpath, so that container will read from the deployment descriptor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:component-scan base-package="com.techiekernel.rest" />

  <bean id="FooBar" class="com.techiekernel.service.FooBarServiceImpl" />

</beans>

Configuration for Spring and RESTEasy:

The integration is going to be in web.xml. Register Spring “ContextLoaderListener” listener class and specify the RESTEasy servlet “org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher“ as front controller for web services.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<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>Spring RESTEasy</display-name>

  <context-param>
    <param-name>resteasy.resources</param-name>
    <param-value>com.techiekernel.rest.FooBarWS</param-value>
  </context-param>
 
  <listener>
    <listener-class>
      org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
                </listener-class>
  </listener>
 
  <listener>
    <listener-class>
                        org.springframework.web.context.ContextLoaderListener
                </listener-class>
  </listener>
 
 <!-- this need same with resteasy servlet url-pattern -->
  <context-param>
    <param-name>resteasy.servlet.mapping.prefix</param-name>
    <param-value>/ws</param-value>
  </context-param>
  <servlet>
    <servlet-name>resteasy-servlet</servlet-name>
    <servlet-class>
      org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
                </servlet-class>
  </servlet>
 
  <servlet-mapping>
    <servlet-name>resteasy-servlet</servlet-name>
    <url-pattern>/ws/*</url-pattern>
  </servlet-mapping>
</web-app>

Application Deployment:

Now every thing is ready and time to build for creating a war file.

1
mvn clean install;

n successful completion of the above command a war file will be get created in target folder. Copy the war file to webapp folder of your tomcat and start the tomcat.

Testing:

Once tomcat is started use the following url to access the web service.

1
http://localhost:8080/Spring-RESTEasy/ws/foobar/techiekernel

Once you hit the url in the browser, you are going to get the following output.


1
FooBar say : techiekernel


Source Code:

You can pull the code from GitHub.