This tutorial demonstrates how exceptions (called escalations in OfficeFloor) are handled within the YAML REST composition when integrating with Spring Boot.
Three levels of exception handling are covered:
@RestControllerAdvice / @ExceptionHandler mechanism.
All three scenarios throw the same checked exception. Declaring it as a checked exception (extends Exception) ensures the throws clause appears in the method signature, which is required for OfficeFloor to discover and route the escalation through the YAML configuration.
public class MockException extends Exception {
public MockException(String message) {
super(message);
}
}
Method escalation routes an exception thrown by a specific method to a handler step declared within the same YAML composition.
The escalations block sits under the step that throws the exception. Each entry maps the fully-qualified exception class name to the name of the handler step to invoke.
service:
class: net.officefloor.spring.starter.rest.exception.MethodService
escalations:
net.officefloor.spring.starter.rest.exception.MockException: handler
handler:
class: net.officefloor.spring.starter.rest.exception.MethodExceptionHandler
When service throws MockException, OfficeFloor routes execution to the handler step rather than letting the exception propagate further.
The service method simply declares and throws the exception:
public class MethodService {
public void service() throws MockException {
throw new MockException("thrown");
}
}
The handler receives the thrown exception via the @Parameter annotation, which binds the escalation object as a method parameter. From there it can inspect the exception and write a response using ObjectResponse:
public class MethodExceptionHandler {
public void handle(@Parameter MockException ex, ObjectResponse<String> response) {
response.send("Method handled: " + ex.getMessage());
}
}
The @Parameter annotation is provided by OfficeFloor and signals that the parameter value is passed from the previous step's output — in the escalation case this is the thrown exception.
Composition escalation moves the escalation routing up to the composition level. Rather than declaring it on a single method, it applies to every method in the composition. This is useful when multiple service steps share a common failure mode that should be handled in one place.
The composition block at the top of the YAML file holds the shared escalation mappings:
composition:
escalations:
net.officefloor.spring.starter.rest.exception.MockException: handler
service:
class: net.officefloor.spring.starter.rest.exception.CompositionService
handler:
class: net.officefloor.spring.starter.rest.exception.CompositionExceptionHandler
Any step in the composition that throws MockException — whether it is service or any other step added later — is automatically routed to the handler step.
The service class is identical in structure to the method-escalation example:
public class CompositionService {
public void service() throws MockException {
throw new MockException("thrown");
}
}
The handler class is also the same pattern, just producing a different response to illustrate which level caught the exception:
public class CompositionExceptionHandler {
public void handle(@Parameter MockException ex, ObjectResponse<String> response) {
response.send("Composition handled: " + ex.getMessage());
}
}
The key difference from method escalation is entirely in the YAML — the Java classes are written the same way regardless of which level handles the escalation.
When neither a method nor a composition escalation is configured, the exception propagates out of the OfficeFloor composition and is handled by Spring's standard exception handling infrastructure. This allows OfficeFloor REST endpoints to participate in an application's existing @RestControllerAdvice setup without any extra configuration.
The YAML for this endpoint needs no escalation configuration at all:
service: class: net.officefloor.spring.starter.rest.exception.SpringAdviceService
The service method throws the exception in the same way as before:
public class SpringAdviceService {
public void service() throws MockException {
throw new MockException("thrown");
}
}
The @RestControllerAdvice class provides the @ExceptionHandler method. Spring resolves the HTTP response status via @ResponseStatus:
@RestControllerAdvice
public class ExceptionControllerAdvice {
@ExceptionHandler(MockException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleMockException(MockException ex) {
return "Spring handled: " + ex.getMessage();
}
}
Because this is a standard Spring mechanism, all the usual Spring features apply — returning ResponseEntity, reading the HttpServletRequest, accessing Spring beans, and so on.
| Approach | When to use |
| Method escalation | Handle an exception only for a specific step in the composition — for example a step with its own distinct recovery path. |
| Composition escalation | Handle an exception the same way for every step in the composition — common for infrastructure errors such as database timeouts. |
| Spring ControllerAdvice | Re-use existing application-wide Spring exception handling, or integrate with Spring libraries that register their own advice. |
The tests use Spring Boot's MockMvc to call each endpoint and assert the response:
package net.officefloor.spring.starter.rest.exception;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
public class ExceptionHandlingTutorialTest {
@Autowired
protected MockMvc mvc;
@Test
public void method_exception_handling() throws Exception {
this.mvc.perform(get("/exception/method"))
.andExpect(status().isOk())
.andExpect(content().string("Method handled: thrown"));
}
@Test
public void composition_exception_handling() throws Exception {
this.mvc.perform(get("/exception/composition"))
.andExpect(status().isOk())
.andExpect(content().string("Composition handled: thrown"));
}
@Test
public void spring_controller_advice() throws Exception {
this.mvc.perform(get("/exception/spring"))
.andExpect(status().isBadRequest())
.andExpect(content().string("Spring handled: thrown"));
}
}
The Spring REST Security tutorial demonstrates Spring Security integration with OfficeFloor REST YAML composition.