Create JAX-RS Server
This chapter publishes a small employee API with CXF's JAX-RS frontend. The resource returns JSON, accepts a new employee, and runs on the embedded Jetty transport so it can be tested immediately.
Dependencies
<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-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.17.0</version>
</dependency>
Model and resource
package com.example.rest;
public class Employee {
private int id;
private String name;
private String role;
public Employee() {
}
public Employee(int id, String name, String role) {
this.id = id;
this.name = name;
this.role = role;
}
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; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
}
package com.example.rest;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.ArrayList;
import java.util.List;
@Path("/employees")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class EmployeeResource {
private final List<Employee> employees = new ArrayList<>();
private int nextId = 1;
@GET
public List<Employee> list() {
return employees;
}
@GET
@Path("/{id}")
public Employee find(@PathParam("id") int id) {
return employees.stream()
.filter(e -> e.getId() == id)
.findFirst()
.orElseThrow(() -> new NotFoundException("No employee " + id));
}
@POST
public Employee create(Employee employee) {
employee.setId(nextId++);
employees.add(employee);
return employee;
}
}
The class-level annotations apply to every method. The @Path on find adds a segment, so the final URL is /employees/{id}.
Publish the endpoint
package com.example.rest;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
public class RestServer {
public static void main(String[] args) {
JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
factory.setResourceClasses(EmployeeResource.class);
factory.setResourceProvider(
EmployeeResource.class,
new SingletonResourceProvider(new EmployeeResource()));
factory.setProvider(new JacksonJsonProvider());
factory.setAddress("http://localhost:8080/api");
factory.create();
System.out.println("API: http://localhost:8080/api/employees");
}
}
A singleton resource keeps the in-memory list between requests, which is convenient for a demo. Production resources usually delegate to a repository or service and stay stateless.
Exercise the API
curl -s http://localhost:8080/api/employees
curl -s -X POST http://localhost:8080/api/employees \
-H "Content-Type: application/json" \
-d '{"name":"Ada","role":"engineer"}'
curl -s http://localhost:8080/api/employees/1
Root and service listings
CXF can expose a service listing page for development. It is useful while learning, but disable it in production because it reveals endpoint names.
factory.setStaticSubresourceResolution(true);
Keep API paths stable and version them when a breaking change is needed. The base address is the version boundary: /api/v1 in front of the resource path makes future migrations easier.
Error responses
Returning a WebApplicationException produces the corresponding status code. For structured JSON errors, register an exception mapper as shown in the JAX-RS overview. Clients should receive the same error shape from every endpoint.