Skip to content

What it is

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.

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.

Installation

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>

Getting started

The smallest useful thing you can do with it, and what each part means.

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.

Advanced usage

Where the library earns its place over a simpler alternative.

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.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

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.

Background

Why it exists, and what it was reacting to.

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.