Building a RESTful web service with JAX-RS in NetBeans using Jersey

JAX-RS gives Java web services a compact, annotation-based HTTP contract, while Jersey provides the runtime that discovers resources and converts Java objects into HTTP responses. NetBeans supplies project navigation, editing and local deployment tools, but Maven dependencies and compatible namespace choices determine whether the service starts.

This walkthrough creates a Maven WAR for a JSON API of Java training sessions. It covers resource classes, status codes, validation, local calls and packaging. The example uses Java records, so select JDK 17 and make the compiler release match the installed runtime.

Generated clients and service stubs can accelerate early development, yet a small, explicit Maven setup remains easier to debug. That matters when operations and development teams in different cities must produce the same artefact for staging and production.

Prepare a Maven web application

In NetBeans, create a Maven web application configured to package as a WAR, using com.example.events as the group and events as the artefact name. Place resources under src/main/java and give the WAR a stable finalName of events. This keeps the local URL predictable even when the project version changes.

Add Jersey’s servlet core, JSON Jackson provider and HK2 dependencies, preferably through jersey-bom. A new Jakarta project should use Jersey 3.x and jakarta.ws.rs imports; an older javax.ws.rs application needs Jersey 2.x and a compatible deployment stack. Mixing those namespaces causes startup or class-loading failures. Add Jersey’s Bean Validation integration if requests need field-level constraints.

Define the JSON contract

Start with an immutable data-transfer object rather than exposing a database entity directly:

public record TrainingSession(
    String id,
    String title,
    String city,
    OffsetDateTime startsAt,
    int seats
) {}

Jackson can serialise this record as JSON. An OffsetDateTime preserves the meeting’s offset, while storing the underlying instant in UTC supports reliable comparisons and filtering.

Create an application class that sets the base path and registers the resource package:

@ApplicationPath("/api")
public class EventsApplication extends ResourceConfig {
    public EventsApplication() {
        packages("com.example.events.api");
        register(JacksonFeature.class);
    }
}

Register an ObjectMapper context resolver if the application needs custom modules, including JavaTimeModule. Keep environment-specific values outside source code. A useful comparison is this grouped settings example, which demonstrates hierarchical INI values in Python; a Java service can apply the same separation with properties files, YAML or environment variables.

Implement resource operations

Arrange session operations under /sessions. Collection retrieval accepts an optional city filter, individual retrieval uses /{id}, and creation, replacement and deletion operate on the same resource representation.

The core creation pattern returns the correct status and location:

@Path("/sessions")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class SessionResource {
    private final SessionRepository sessions;

    public SessionResource(SessionRepository sessions) {
        this.sessions = sessions;
    }

    @GET
    public List<TrainingSession> list(@QueryParam("city") String city) {
        return sessions.findByCity(city);
    }

    @POST
    public Response add(@Valid TrainingSession session,
                        @Context UriInfo uri) {
        TrainingSession saved = sessions.add(session);
        return Response.created(uri.getAbsolutePathBuilder()
                .path(saved.id()).build())
                .entity(saved).build();
    }
}

Supply the repository through an HK2 binding. Add @GET, @PUT and @DELETE methods with @PathParam, returning 404 for missing sessions and 204 after a successful deletion. For event-driven ingestion or streaming clients, this RxJava NetBeans example shows reactive pipeline development in the IDE.

Apply @Valid, @NotBlank and @Min constraints to the input model. Map validation failures and unexpected exceptions to a consistent JSON error representation without exposing stack traces, SQL details or internal hostnames.

Run the API locally

Run the WAR on NetBeans’ configured Tomcat installation. Because the Maven finalName is events, a filtered collection request should resemble:

curl -i "http://localhost:8080/events/api/sessions?city=Sydney"

A successful response returns 200 and a JSON array. Test creation, retrieval, replacement and deletion through NetBeans’ REST client or curl; creation should return 201 Created with a Location header containing the new resource URL.

Seed and query data with Australian cities such as Sydney, Melbourne and Brisbane. Account for local clock rules: Sydney normally moves between AEST and AEDT, while Brisbane remains on AEST. Persist instants in UTC and return ISO-8601 timestamps with explicit offsets so a session scheduled for an arvo in Melbourne does not move when a client changes locale.

Package and operate the service

Before packaging, commit the reproducible project state; this NetBeans Subversion workflow shows how to add, update and inspect files while excluding generated directories such as target. Build the WAR with mvn clean package and deploy it to a servlet container compatible with the selected Jersey generation.

Before promotion, require evidence for each release condition:

  • A clean checkout passes mvn clean verify.
  • Contract tests cover valid input, malformed JSON, empty searches and missing IDs.
  • Database migrations are versioned and complete before the new WAR receives traffic.
  • Logs and HTTP responses contain no stack traces, credentials or personal records.

After deployment, operational controls should remain explicit:

  • Authentication and authorisation protect every path that is not deliberately public.
  • CORS lists approved browser origins rather than combining wildcards with credentials.
  • Health checks and request metrics feed the monitoring used by the on-call team.
  • Errors use a consistent JSON problem format with safe, actionable messages.

Australian data-handling checks should include the Privacy Act 1988 and Australian Privacy Principles where applicable. Procurement teams commonly evaluate cloud regions in Sydney or Melbourne and may require backups and support access to remain in Australia; verify those contractual boundaries. Keep documentation in Australian English, including words such as “authorised” and “behaviour”, while keeping API dates and numbers locale-neutral.