Skip to main content

juneau-shaded-rest-server

The juneau-shaded-rest-server artifact bundles all core Juneau modules plus REST server functionality. At 3.8 MB, it provides everything needed for building REST server applications.

What's Included

This artifact includes:

  • All modules from juneau-shaded-core (marshalling, config, testing)
  • juneau-rest-common - REST APIs common to client and server
  • juneau-rest-server - Full-featured REST servlet API
  • juneau-rest-server-rdf - RDF support for REST servers
  • juneau-rest-mock - REST testing utilities

Use Cases

Use juneau-shaded-rest-server when you need:

  • REST API development - Create RESTful web services
  • Servlet-based applications - Deploy to any Servlet 6.1+ container
  • Automatic content negotiation - Support multiple formats automatically
  • Swagger/OpenAPI integration - Built-in API documentation
  • Testing REST services - Use the mock framework

Maven Dependency

<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-shaded-rest-server</artifactId>
<version>${juneau.version}</version>
</dependency>

Bazel Dependency

maven_jar(
name = "juneau_rest_server",
artifact = "org.apache.juneau:juneau-shaded-rest-server:${juneau.version}",
)

# External dependencies
maven_jar(
name = "jakarta_servlet_api",
artifact = "jakarta.servlet:jakarta.servlet-api:6.1.0",
)

maven_jar(
name = "jetty_server",
artifact = "org.eclipse.jetty:jetty-server:12.0.5",
)

java_library(
name = "my_rest_api",
srcs = glob(["src/**/*.java"]),
deps = [
"@juneau_rest_server//jar",
"@jakarta_servlet_api//jar",
"@jetty_server//jar",
],
)

External Dependencies

juneau-shaded-rest-server requires:

Required

  • Jakarta Servlet API 6.1+ - Servlet specification

Optional

  • Jakarta XML Bind API 3.0+ - For XML serialization
  • Apache Jena - For RDF support
  • Servlet Container - Jetty, Tomcat, etc. (runtime dependency)

Example Usage

Basic REST Resource

import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.servlet.*;

@Rest(
path="/petstore",
title="Petstore API",
description="Sample REST API"
)
public class PetStoreResource extends BasicRestServlet {

@RestGet(path="/pets")
public List<Pet> getPets() {
return petService.getAllPets();
}

@RestGet(path="/pets/{id}")
public Pet getPet(@Path int id) {
return petService.getPet(id);
}

@RestPost(path="/pets")
public Pet createPet(@Content Pet pet) {
return petService.createPet(pet);
}

@RestPut(path="/pets/{id}")
public Pet updatePet(@Path int id, @Content Pet pet) {
return petService.updatePet(id, pet);
}

@RestDelete(path="/pets/{id}")
public void deletePet(@Path int id) {
petService.deletePet(id);
}
}

Automatic Content Negotiation

Juneau automatically serializes responses based on the Accept header:

@RestGet(path="/person/{id}")
public Person getPerson(@Path int id) {
return new Person("John Smith", 42);
}

// GET /person/1
// Accept: application/json → {"name":"John Smith","age":42}
// Accept: text/xml → <object><name>John Smith</name><age>42</age></object>
// Accept: text/html → <table>...</table>
// Accept: text/plain → name=John Smith, age=42

Request Body Parsing

@RestPost(path="/person")
public Person createPerson(@Content Person person) {
// person is automatically parsed from JSON, XML, etc.
return personService.create(person);
}

// POST /person
// Content-Type: application/json
// Body: {"name":"John","age":42}

Path Parameters

@RestGet(path="/users/{userId}/posts/{postId}")
public Post getUserPost(
@Path("userId") int userId,
@Path("postId") int postId
) {
return postService.getPost(userId, postId);
}

Query Parameters

@RestGet(path="/search")
public List<Result> search(
@Query("q") String query,
@Query("limit") @Default("10") int limit,
@Query("offset") @Default("0") int offset
) {
return searchService.search(query, limit, offset);
}

// GET /search?q=juneau&limit=20&offset=40

Request/Response Headers

@RestGet(path="/data")
public String getData(
@Header("User-Agent") String userAgent,
RestResponse res
) {
res.setHeader("X-Custom", "value");
return "data";
}

Swagger/OpenAPI Integration

@Rest(
path="/api",
swagger=@Swagger(
title="My API",
version="1.0",
description="API Description",
contact=@Contact(name="Support", email="support@example.com")
)
)
public class MyApi extends BasicRestServlet {

@RestGet(
path="/items",
summary="Get all items",
description="Returns a list of all items"
)
public List<Item> getItems() {
return itemService.getAll();
}
}

// Swagger UI available at: /api/swagger
// OpenAPI JSON at: /api/openapi.json

Exception Handling

@RestGet(path="/person/{id}")
public Person getPerson(@Path int id) throws NotFound {
Person person = personService.getPerson(id);
if (person == null)
throw new NotFound("Person not found: {0}", id);
return person;
}

// Returns HTTP 404 with error message

Guards and Matchers

@Rest(path="/admin")
@RestGuard(AdminGuard.class) // Require admin role
public class AdminResource extends BasicRestServlet {

@RestGet(path="/users")
public List<User> getUsers() {
return userService.getAllUsers();
}
}

public class AdminGuard extends RestGuard {
@Override
public boolean guard(RestRequest req, RestResponse res) {
return req.isUserInRole("admin");
}
}

Converters

@Rest(
path="/api",
converters={Queryable.class, Introspectable.class}
)
public class MyApi extends BasicRestServlet {

@RestGet(path="/people")
public List<Person> getPeople() {
return personService.getAll();
}

// GET /people?s=age>21&v=name,age&o=age
// Returns filtered, sorted, and projected results
}

Configuration

@Rest(
path="/api",
config="myapi.cfg", // External config file
defaultAccept="application/json",
defaultContentType="application/json",
maxInput="10M" // Limit request size
)
public class MyApi extends BasicRestServlet {
// ...
}

Features

Built-in Serializers/Parsers

  • JSON (multiple flavors)
  • XML
  • HTML (with customizable styling)
  • URL-Encoding
  • MessagePack
  • OpenAPI
  • Plain Text
  • CSV

Automatic Features

  • Content negotiation
  • Character encoding detection
  • GZIP compression
  • Request/response logging
  • Exception mapping
  • Bean validation

Advanced Features

  • Role-based security
  • Method-level guards
  • Request/response interceptors
  • Custom converters
  • File uploads/downloads
  • Server-sent events
  • HTTP/2 support (via container)

Testing with Mock Framework

@Test
public void testRestEndpoint() throws Exception {
MockRestClient client = MockRestClient.create(MyRestApi.class)
.json()
.build();

// Test GET
Person person = client.get("/person/1")
.run()
.assertStatus().is(200)
.getContent().as(Person.class);

assertEquals("John", person.getName());

// Test POST
Person newPerson = new Person("Jane", 30);
client.post("/person")
.content(newPerson)
.run()
.assertStatus().is(201);
}

Performance Characteristics

  • Streaming: Efficient memory usage for large requests/responses
  • Thread-Safe: All serializers/parsers are thread-safe
  • Caching: Bean metadata is cached for performance
  • Minimal Overhead: No reflection during serialization/parsing

Migration from Individual Modules

Before:

<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-marshall</artifactId>
<version>${juneau.version}</version>
</dependency>
<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-rest-server</artifactId>
<version>${juneau.version}</version>
</dependency>
<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-rest-common</artifactId>
<version>${juneau.version}</version>
</dependency>

After:

<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-shaded-rest-server</artifactId>
<version>${juneau.version}</version>
</dependency>

Next Steps

Discussion

Share feedback or follow-up questions for this page directly through GitHub.