Jersey

Language: Java

Web

Jersey was developed by Oracle (originally as part of GlassFish) to provide a standard, annotation-driven framework for creating RESTful web services on the Java platform. It supports both client-side and server-side APIs, integrates with CDI (Contexts and Dependency Injection), and provides filters, exception handling, and advanced features such as asynchronous processing. Over the years, Jersey has become a widely used framework in enterprise and microservice applications due to its adherence to standards and ease of integration.

Jersey is the reference implementation of JAX-RS (Java API for RESTful Web Services) for building RESTful web services in Java. It simplifies creating web services with annotations, dependency injection, and automatic JSON/XML support.

Installation

maven: Add the following dependencies in your pom.xml: <dependency> <groupId>org.glassfish.jersey.core</groupId> <artifactId>jersey-server</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-binding</artifactId> <version>3.2.0</version> </dependency>
gradle: Add the following dependencies in build.gradle: implementation 'org.glassfish.jersey.core:jersey-server:3.2.0' implementation 'org.glassfish.jersey.containers:jersey-container-servlet:3.2.0' implementation 'org.glassfish.jersey.media:jersey-media-json-binding:3.2.0'

Usage

Jersey allows you to define REST endpoints using annotations such as @Path, @GET, @POST, @PUT, @DELETE, and @Produces/@Consumes for content negotiation. It supports JSON/XML serialization, request/response filtering, exception mapping, dependency injection via CDI, asynchronous endpoints, and client-side APIs for consuming other REST services.

Simple REST GET endpoint

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/hello")
public class HelloResource {
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "Hello Jersey!";
    }
}

Defines a basic GET endpoint that returns a plain text response at the path `/hello`.

POST endpoint with JSON input

import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/users")
public class UserResource {
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public User createUser(User user) {
        // save user to database or service
        return user;
    }
}

Defines a POST endpoint that accepts a JSON object, processes it, and returns a JSON response.

Using Dependency Injection (CDI)

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/service")
public class ServiceResource {
    @Inject
    MyService myService;

    @GET
    public String getMessage() {
        return myService.getMessage();
    }
}

Demonstrates injecting a service bean into a Jersey resource using CDI.

Exception Mapping

import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import jakarta.ws.rs.core.Response;

@Provider
public class GenericExceptionMapper implements ExceptionMapper<Exception> {
    @Override
    public Response toResponse(Exception exception) {
        return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                       .entity(exception.getMessage())
                       .build();
    }
}

Custom exception mapper that catches all exceptions thrown by endpoints and returns a structured HTTP response.

Filtering Requests and Responses

import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.ext.Provider;

@Provider
public class LoggingFilter implements ContainerRequestFilter {
    @Override
    public void filter(ContainerRequestContext requestContext) {
        System.out.println("Incoming request: " + requestContext.getUriInfo().getRequestUri());
    }
}

Implements a request filter to log all incoming requests.

Asynchronous Endpoint

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.container.AsyncResponse;
import jakarta.ws.rs.container.Suspended;
import java.util.concurrent.CompletableFuture;

@Path("/async")
public class AsyncResource {
    @GET
    public void asyncHello(@Suspended AsyncResponse response) {
        CompletableFuture.supplyAsync(() -> "Hello Async Jersey!")
                         .thenAccept(response::resume);
    }
}

Demonstrates asynchronous processing with @Suspended AsyncResponse and CompletableFuture.

Error Handling

WebApplicationException: Thrown when you want to return a specific HTTP status code. Use proper response codes and messages.
BadRequestException: Thrown when the request cannot be processed due to client error (400). Validate inputs before processing.
ProcessingException: Occurs during client-side API calls. Check network connectivity and request configuration.

Best Practices

Use standard JAX-RS annotations for REST endpoints to maintain portability.

Leverage CDI for dependency injection and modular design.

Use ExceptionMappers to handle errors consistently.

Use Filters and Interceptors for cross-cutting concerns like logging, authentication, and metrics.

Separate resource classes, services, and domain models for maintainable architecture.