From 06ceb4d87c05adcad8551f9cf0037bc4b3a0c32f Mon Sep 17 00:00:00 2001 From: Saptarshi Basu Date: Thu, 12 Jan 2017 19:25:18 +0530 Subject: [PATCH] JAX-RS API using Jersey [BAEL-558] (#956) * WatchService vs. Apache Commons IO Mnitoring * Indentation fixed * Indentation fixed * JAX-RS API using Jersey [BAEL-558] * JAX-RS API using Jersey [BAEL-558] * Modifications made to remove xml * applicationContext.xml removed * All try catch moved to ExceptionMapper * fixes * review comments incorporated * module renamed --- pom.xml | 1 + spring-jersey/.gitignore | 13 ++ spring-jersey/README.md | 3 + spring-jersey/pom.xml | 210 ++++++++++++++++++ .../server/config/ApplicationInitializer.java | 22 ++ .../baeldung/server/config/RestConfig.java | 19 ++ .../AlreadyExistsExceptionHandler.java | 12 + .../exception/EmployeeAlreadyExists.java | 5 + .../server/exception/EmployeeNotFound.java | 5 + .../exception/NotFoundExceptionHandler.java | 12 + .../com/baeldung/server/model/Employee.java | 34 +++ .../server/repository/EmployeeRepository.java | 18 ++ .../repository/EmployeeRepositoryImpl.java | 65 ++++++ .../server/rest/EmployeeResource.java | 64 ++++++ spring-jersey/src/main/resources/logback.xml | 15 ++ .../baeldung/server/JerseyApiLiveTest.java | 91 ++++++++ 16 files changed, 589 insertions(+) create mode 100644 spring-jersey/.gitignore create mode 100644 spring-jersey/README.md create mode 100644 spring-jersey/pom.xml create mode 100644 spring-jersey/src/main/java/com/baeldung/server/config/ApplicationInitializer.java create mode 100644 spring-jersey/src/main/java/com/baeldung/server/config/RestConfig.java create mode 100644 spring-jersey/src/main/java/com/baeldung/server/exception/AlreadyExistsExceptionHandler.java create mode 100644 spring-jersey/src/main/java/com/baeldung/server/exception/EmployeeAlreadyExists.java create mode 100644 spring-jersey/src/main/java/com/baeldung/server/exception/EmployeeNotFound.java create mode 100644 spring-jersey/src/main/java/com/baeldung/server/exception/NotFoundExceptionHandler.java create mode 100644 spring-jersey/src/main/java/com/baeldung/server/model/Employee.java create mode 100644 spring-jersey/src/main/java/com/baeldung/server/repository/EmployeeRepository.java create mode 100644 spring-jersey/src/main/java/com/baeldung/server/repository/EmployeeRepositoryImpl.java create mode 100644 spring-jersey/src/main/java/com/baeldung/server/rest/EmployeeResource.java create mode 100644 spring-jersey/src/main/resources/logback.xml create mode 100644 spring-jersey/src/test/java/com/baeldung/server/JerseyApiLiveTest.java diff --git a/pom.xml b/pom.xml index c590183137..0b0351f462 100644 --- a/pom.xml +++ b/pom.xml @@ -117,6 +117,7 @@ spring-hibernate3 spring-hibernate4 spring-integration + spring-jersey spring-jms spring-jooq spring-jpa diff --git a/spring-jersey/.gitignore b/spring-jersey/.gitignore new file mode 100644 index 0000000000..83c05e60c8 --- /dev/null +++ b/spring-jersey/.gitignore @@ -0,0 +1,13 @@ +*.class + +#folders# +/target +/neoDb* +/data +/src/main/webapp/WEB-INF/classes +*/META-INF/* + +# Packaged files # +*.jar +*.war +*.ear \ No newline at end of file diff --git a/spring-jersey/README.md b/spring-jersey/README.md new file mode 100644 index 0000000000..2767ceb9a7 --- /dev/null +++ b/spring-jersey/README.md @@ -0,0 +1,3 @@ +========= + +## REST API with Jersey & Spring Example Project diff --git a/spring-jersey/pom.xml b/spring-jersey/pom.xml new file mode 100644 index 0000000000..00d67febec --- /dev/null +++ b/spring-jersey/pom.xml @@ -0,0 +1,210 @@ + + + 4.0.0 + + com.baeldung + jersey-api + 0.1-SNAPSHOT + war + + + 2.25 + 1.7.22 + 1.1.8 + 4.12 + 3.0.0 + 2.19.1 + 1.6.1 + 4.12 + 4.4.5 + 4.5.2 + 3.1.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + maven-war-plugin + ${maven-war-plugin.version} + + false + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + org.codehaus.cargo + cargo-maven2-plugin + ${cargo-maven2-plugin.version} + + true + + jetty8x + embedded + + + + 8082 + + + + + + + + + + + + + org.glassfish.jersey.core + jersey-server + ${jersey.version} + + + org.glassfish.jersey.containers + jersey-container-servlet + ${jersey.version} + + + org.glassfish.jersey.media + jersey-media-json-jackson + ${jersey.version} + + + + javax.servlet + javax.servlet-api + ${servlet-api-version} + provided + + + + + org.glassfish.jersey.ext + jersey-spring3 + ${jersey.version} + + + commons-logging + commons-logging + + + + + + org.slf4j + jcl-over-slf4j + ${jcl.slf4j.version} + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + test + + + + org.apache.httpcomponents + httpcore + ${httpcore.version} + test + + + + + + junit + junit + ${junit.version} + test + + + + + + live + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*IntegrationTest.java + + + **/*LiveTest.java + + + + + + + json + + + + + org.codehaus.cargo + cargo-maven2-plugin + ${cargo-maven2-plugin.version} + + false + + + + start-server + pre-integration-test + + start + + + + stop-server + post-integration-test + + stop + + + + + + + + + \ No newline at end of file diff --git a/spring-jersey/src/main/java/com/baeldung/server/config/ApplicationInitializer.java b/spring-jersey/src/main/java/com/baeldung/server/config/ApplicationInitializer.java new file mode 100644 index 0000000000..d91d4d5f38 --- /dev/null +++ b/spring-jersey/src/main/java/com/baeldung/server/config/ApplicationInitializer.java @@ -0,0 +1,22 @@ +package com.baeldung.server.config; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.web.WebApplicationInitializer; +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +@Order(Ordered.HIGHEST_PRECEDENCE) +public class ApplicationInitializer implements WebApplicationInitializer { + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + servletContext.addListener(new ContextLoaderListener(context)); + servletContext.setInitParameter("contextConfigLocation", "com.baeldung.server"); + } + +} diff --git a/spring-jersey/src/main/java/com/baeldung/server/config/RestConfig.java b/spring-jersey/src/main/java/com/baeldung/server/config/RestConfig.java new file mode 100644 index 0000000000..34d8948f59 --- /dev/null +++ b/spring-jersey/src/main/java/com/baeldung/server/config/RestConfig.java @@ -0,0 +1,19 @@ +package com.baeldung.server.config; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +import com.baeldung.server.exception.AlreadyExistsExceptionHandler; +import com.baeldung.server.exception.NotFoundExceptionHandler; +import com.baeldung.server.rest.EmployeeResource; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +@ApplicationPath("/resources") +public class RestConfig extends Application { + public Set> getClasses() { + return new HashSet>(Arrays.asList(EmployeeResource.class, NotFoundExceptionHandler.class, AlreadyExistsExceptionHandler.class)); + } +} \ No newline at end of file diff --git a/spring-jersey/src/main/java/com/baeldung/server/exception/AlreadyExistsExceptionHandler.java b/spring-jersey/src/main/java/com/baeldung/server/exception/AlreadyExistsExceptionHandler.java new file mode 100644 index 0000000000..4603372807 --- /dev/null +++ b/spring-jersey/src/main/java/com/baeldung/server/exception/AlreadyExistsExceptionHandler.java @@ -0,0 +1,12 @@ +package com.baeldung.server.exception; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +@Provider +public class AlreadyExistsExceptionHandler implements ExceptionMapper { + public Response toResponse(EmployeeAlreadyExists ex) { + return Response.status(Response.Status.CONFLICT.getStatusCode()).build(); + } +} diff --git a/spring-jersey/src/main/java/com/baeldung/server/exception/EmployeeAlreadyExists.java b/spring-jersey/src/main/java/com/baeldung/server/exception/EmployeeAlreadyExists.java new file mode 100644 index 0000000000..827e4bf1e7 --- /dev/null +++ b/spring-jersey/src/main/java/com/baeldung/server/exception/EmployeeAlreadyExists.java @@ -0,0 +1,5 @@ +package com.baeldung.server.exception; + +public class EmployeeAlreadyExists extends RuntimeException { + +} diff --git a/spring-jersey/src/main/java/com/baeldung/server/exception/EmployeeNotFound.java b/spring-jersey/src/main/java/com/baeldung/server/exception/EmployeeNotFound.java new file mode 100644 index 0000000000..f205b5dfae --- /dev/null +++ b/spring-jersey/src/main/java/com/baeldung/server/exception/EmployeeNotFound.java @@ -0,0 +1,5 @@ +package com.baeldung.server.exception; + +public class EmployeeNotFound extends RuntimeException { + +} diff --git a/spring-jersey/src/main/java/com/baeldung/server/exception/NotFoundExceptionHandler.java b/spring-jersey/src/main/java/com/baeldung/server/exception/NotFoundExceptionHandler.java new file mode 100644 index 0000000000..5de9b53c30 --- /dev/null +++ b/spring-jersey/src/main/java/com/baeldung/server/exception/NotFoundExceptionHandler.java @@ -0,0 +1,12 @@ +package com.baeldung.server.exception; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +@Provider +public class NotFoundExceptionHandler implements ExceptionMapper { + public Response toResponse(EmployeeNotFound ex) { + return Response.status(Response.Status.NOT_FOUND).build(); + } +} diff --git a/spring-jersey/src/main/java/com/baeldung/server/model/Employee.java b/spring-jersey/src/main/java/com/baeldung/server/model/Employee.java new file mode 100644 index 0000000000..1801134f68 --- /dev/null +++ b/spring-jersey/src/main/java/com/baeldung/server/model/Employee.java @@ -0,0 +1,34 @@ +package com.baeldung.server.model; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class Employee { + private int id; + private String firstName; + + public Employee() { + + } + + public Employee(int id, String firstName) { + this.id = id; + this.firstName = firstName; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } +} diff --git a/spring-jersey/src/main/java/com/baeldung/server/repository/EmployeeRepository.java b/spring-jersey/src/main/java/com/baeldung/server/repository/EmployeeRepository.java new file mode 100644 index 0000000000..15132cd618 --- /dev/null +++ b/spring-jersey/src/main/java/com/baeldung/server/repository/EmployeeRepository.java @@ -0,0 +1,18 @@ +package com.baeldung.server.repository; + +import java.util.List; + +import com.baeldung.server.model.Employee; + +public interface EmployeeRepository { + + public List getAllEmployees(); + + public Employee getEmployee(int id); + + public void updateEmployee(Employee employee, int id); + + public void deleteEmployee(int id); + + public void addEmployee(Employee employee); +} diff --git a/spring-jersey/src/main/java/com/baeldung/server/repository/EmployeeRepositoryImpl.java b/spring-jersey/src/main/java/com/baeldung/server/repository/EmployeeRepositoryImpl.java new file mode 100644 index 0000000000..8e61e1395b --- /dev/null +++ b/spring-jersey/src/main/java/com/baeldung/server/repository/EmployeeRepositoryImpl.java @@ -0,0 +1,65 @@ +package com.baeldung.server.repository; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.baeldung.server.exception.EmployeeAlreadyExists; +import com.baeldung.server.exception.EmployeeNotFound; +import com.baeldung.server.model.Employee; + +@Component +public class EmployeeRepositoryImpl implements EmployeeRepository { + private List employeeList; + + public EmployeeRepositoryImpl() { + employeeList = new ArrayList(); + employeeList.add(new Employee(1, "Jane")); + employeeList.add(new Employee(2, "Jack")); + employeeList.add(new Employee(3, "George")); + } + + public List getAllEmployees() { + return employeeList; + } + + public Employee getEmployee(int id) { + for (Employee emp : employeeList) { + if (emp.getId() == id) { + return emp; + } + } + throw new EmployeeNotFound(); + } + + public void updateEmployee(Employee employee, int id) { + for (Employee emp : employeeList) { + if (emp.getId() == id) { + emp.setId(employee.getId()); + emp.setFirstName(employee.getFirstName()); + return; + } + } + throw new EmployeeNotFound(); + } + + public void deleteEmployee(int id) { + for (Employee emp : employeeList) { + if (emp.getId() == id) { + employeeList.remove(emp); + return; + } + } + throw new EmployeeNotFound(); + } + + public void addEmployee(Employee employee) { + for (Employee emp : employeeList) { + if (emp.getId() == employee.getId()) { + throw new EmployeeAlreadyExists(); + } + } + employeeList.add(employee); + } +} diff --git a/spring-jersey/src/main/java/com/baeldung/server/rest/EmployeeResource.java b/spring-jersey/src/main/java/com/baeldung/server/rest/EmployeeResource.java new file mode 100644 index 0000000000..2301f3eaf3 --- /dev/null +++ b/spring-jersey/src/main/java/com/baeldung/server/rest/EmployeeResource.java @@ -0,0 +1,64 @@ +package com.baeldung.server.rest; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.springframework.beans.factory.annotation.Autowired; + +import com.baeldung.server.model.Employee; +import com.baeldung.server.repository.EmployeeRepository; + +@Path("/employees") +public class EmployeeResource { + + @Autowired + private EmployeeRepository employeeRepository; + + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public List getAllEmployees() { + return employeeRepository.getAllEmployees(); + } + + @GET + @Path("/{id}") + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Employee getEmployee(@PathParam("id") int id) { + return employeeRepository.getEmployee(id); + } + + @PUT + @Path("/{id}") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response updateEmployee(Employee employee, @PathParam("id") int id) { + employeeRepository.updateEmployee(employee, id); + return Response.status(Response.Status.OK.getStatusCode()).build(); + } + + @DELETE + @Path("/{id}") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response deleteEmployee(@PathParam("id") int id) { + employeeRepository.deleteEmployee(id); + return Response.status(Response.Status.OK.getStatusCode()).build(); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response addEmployee(Employee employee, @Context UriInfo uriInfo) { + employeeRepository.addEmployee(new Employee(employee.getId(), employee.getFirstName())); + return Response.status(Response.Status.CREATED.getStatusCode()).header("Location", String.format("%s/%s", uriInfo.getAbsolutePath().toString(), employee.getId())).build(); + } +} diff --git a/spring-jersey/src/main/resources/logback.xml b/spring-jersey/src/main/resources/logback.xml new file mode 100644 index 0000000000..788096686a --- /dev/null +++ b/spring-jersey/src/main/resources/logback.xml @@ -0,0 +1,15 @@ + + + + + web - %date [%thread] %-5level %logger{36} - + %message%n + + + + + + + + + \ No newline at end of file diff --git a/spring-jersey/src/test/java/com/baeldung/server/JerseyApiLiveTest.java b/spring-jersey/src/test/java/com/baeldung/server/JerseyApiLiveTest.java new file mode 100644 index 0000000000..80c4e94b50 --- /dev/null +++ b/spring-jersey/src/test/java/com/baeldung/server/JerseyApiLiveTest.java @@ -0,0 +1,91 @@ +package com.baeldung.server; + +import java.io.IOException; + +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.Test; + +import com.baeldung.server.model.Employee; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JerseyApiLiveTest { + + private static final String SERVICE_URL = "http://localhost:8082/jersey-api/resources/employees"; + + @Test + public void givenGetAllEmployees_whenCorrectRequest_thenResponseCodeSuccess() throws ClientProtocolException, IOException { + final HttpUriRequest request = new HttpGet(SERVICE_URL); + + final HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); + + assert(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK); + } + + @Test + public void givenGetEmployee_whenEmployeeExists_thenResponseCodeSuccess() throws ClientProtocolException, IOException { + final HttpUriRequest request = new HttpGet(SERVICE_URL + "/1"); + + final HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); + + assert(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK); + } + + @Test + public void givenGetEmployee_whenEmployeeDoesNotExist_thenResponseCodeNotFound() throws ClientProtocolException, IOException { + final HttpUriRequest request = new HttpGet(SERVICE_URL + "/1000"); + + final HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); + + assert(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND); + } + + @Test + public void givenGetEmployee_whenJsonRequested_thenCorrectDataRetrieved() throws ClientProtocolException, IOException { + final HttpUriRequest request = new HttpGet(SERVICE_URL + "/1"); + + request.setHeader("Accept", "application/json"); + final HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); + ObjectMapper mapper = new ObjectMapper(); + Employee emp = mapper.readValue(httpResponse.getEntity().getContent(), Employee.class); + + assert(emp.getFirstName().equals("Jane")); + } + + @Test + public void givenAddEmployee_whenJsonRequestSent_thenResponseCodeCreated() throws ClientProtocolException, IOException { + final HttpPost request = new HttpPost(SERVICE_URL); + + Employee emp = new Employee(5, "Johny"); + ObjectMapper mapper = new ObjectMapper(); + String empJson = mapper.writeValueAsString(emp); + StringEntity input = new StringEntity(empJson); + input.setContentType("application/json"); + request.setEntity(input); + final HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); + + assert(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED); + } + + @Test + public void givenAddEmployee_whenRequestForExistingObjectSent_thenResponseCodeConflict() throws ClientProtocolException, IOException { + final HttpPost request = new HttpPost(SERVICE_URL); + + Employee emp = new Employee(1, "Johny"); + ObjectMapper mapper = new ObjectMapper(); + String empJson = mapper.writeValueAsString(emp); + StringEntity input = new StringEntity(empJson); + input.setContentType("application/json"); + request.setEntity(input); + final HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); + + assert(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_CONFLICT); + } + +} \ No newline at end of file