Render reference documentation with Asciidoctor

This commit removes docbook from the documentation toolchain and
instead makes use of asciidoctor to render the reference documentation
in HTML and PDF formats.

The main Gradle build has been refactored with the documentation tasks
and sniffer tasks extracted to their own gradle file in the "gradle"
folder.

A new asciidoctor Spring theme is also used to render the HTML5 backend.

Issue: SPR-14997
This commit is contained in:
Brian Clozel
2017-01-04 17:51:58 +01:00
parent c5bcefbd07
commit 924adaec6c
93 changed files with 1030 additions and 311 deletions

View File

@@ -0,0 +1,244 @@
[[cors]]
= CORS Support
== Introduction
For security reasons, browsers prohibit AJAX calls to resources residing outside the
current origin. For example, as you're checking your bank account in one tab, you
could have the evil.com website open in another tab. The scripts from evil.com should not
be able to make AJAX requests to your bank API (e.g., withdrawing money from your account!)
using your credentials.
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing[Cross-origin resource sharing]
(CORS) is a http://www.w3.org/TR/cors/[W3C specification] implemented by
http://caniuse.com/#feat=cors[most browsers] that allows you to specify in a flexible
way what kind of cross domain requests are authorized, instead of using some less secured
and less powerful hacks like IFRAME or JSONP.
As of Spring Framework 4.2, CORS is supported out of the box. CORS requests
(https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java#L906[including preflight ones with an `OPTIONS` method])
are automatically dispatched to the various registered ``HandlerMapping``s. They handle
CORS preflight requests and intercept CORS simple and actual requests thanks to a
{api-spring-framework}/web/cors/CorsProcessor.html[CorsProcessor]
implementation (https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/cors/DefaultCorsProcessor.java[DefaultCorsProcessor]
by default) in order to add the relevant CORS response headers (like `Access-Control-Allow-Origin`)
based on the CORS configuration you have provided.
[NOTE]
====
Since CORS requests are automatically dispatched, you *do not need* to change the
`DispatcherServlet` `dispatchOptionsRequest` init parameter value; using its default value
(`false`) is the recommended approach.
====
== Controller method CORS configuration
You can add an
{api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
annotation to your `@RequestMapping` annotated handler method in order to enable CORS on
it. By default `@CrossOrigin` allows all origins and the HTTP methods specified in the
`@RequestMapping` annotation:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
----
It is also possible to enable CORS for the whole controller:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
----
In the above example CORS support is enabled for both the `retrieve()` and the `remove()`
handler methods, and you can also see how you can customize the CORS configuration using
`@CrossOrigin` attributes.
You can even use both controller-level and method-level CORS configurations; Spring will
then combine attributes from both annotations to create merged CORS configuration.
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("http://domain2.com")
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
----
== Global CORS configuration
In addition to fine-grained, annotation-based configuration you'll probably want to
define some global CORS configuration as well. This is similar to using filters but can
be declared within Spring MVC and combined with fine-grained `@CrossOrigin` configuration.
By default all origins and `GET`, `HEAD`, and `POST` methods are allowed.
=== JavaConfig
Enabling CORS for the whole application is as simple as:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
----
You can easily change any properties, as well as only apply this CORS configuration to a
specific path pattern:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);
}
}
----
=== XML namespace
The following minimal XML configuration enables CORS for the `/**` path pattern with
the same default properties as with the aforementioned JavaConfig examples:
[source,xml,indent=0]
[subs="verbatim"]
----
<mvc:cors>
<mvc:mapping path="/**" />
</mvc:cors>
----
It is also possible to declare several CORS mappings with customized properties:
[source,xml,indent=0]
[subs="verbatim"]
----
<mvc:cors>
<mvc:mapping path="/api/**"
allowed-origins="http://domain1.com, http://domain2.com"
allowed-methods="GET, PUT"
allowed-headers="header1, header2, header3"
exposed-headers="header1, header2" allow-credentials="false"
max-age="123" />
<mvc:mapping path="/resources/**"
allowed-origins="http://domain1.com" />
</mvc:cors>
----
== Advanced Customization
{api-spring-framework}/web/cors/CorsConfiguration.html[CorsConfiguration]
allows you to specify how the CORS requests should be processed: allowed origins, headers, methods, etc.
It can be provided in various ways:
* {api-spring-framework}/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfiguration-java.util.Map-[`AbstractHandlerMapping#setCorsConfiguration()`]
allows to specify a `Map` with several {api-spring-framework}/web/cors/CorsConfiguration.html[CorsConfiguration]
instances mapped to path patterns like `/api/**`.
* Subclasses can provide their own `CorsConfiguration` by overriding the
`AbstractHandlerMapping#getCorsConfiguration(Object, HttpServletRequest)` method.
* Handlers can implement the {api-spring-framework}/web/cors/CorsConfigurationSource.html[`CorsConfigurationSource`]
interface (like https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java[`ResourceHttpRequestHandler`]
now does) in order to provide a {api-spring-framework}/web/cors/CorsConfiguration.html[CorsConfiguration]
instance for each request.
== Filter based CORS support
In order to support CORS with filter-based security frameworks like
http://projects.spring.io/spring-security/[Spring Security], or
with other libraries that do not support natively CORS, Spring Framework also
provides a http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/filter/CorsFilter.html[`CorsFilter`].
Instead of using `@CrossOrigin` or `WebMvcConfigurer#addCorsMappings(CorsRegistry)`, you
need to register a custom filter defined like bellow:
[source,java,indent=0]
----
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
public class MyCorsFilter extends CorsFilter {
public MyCorsFilter() {
super(configurationSource());
}
private static UrlBasedCorsConfigurationSource configurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
----
You need to ensure that `CorsFilter` is ordered before the other filters, see
https://spring.io/blog/2015/06/08/cors-support-in-spring-framework#filter-based-cors-support[this blog post]
about how to configure Spring Boot accordingly.

View File

@@ -0,0 +1,244 @@
==== Functional Programming Model
NOTE: This section is to be merged into `web-flux.adoc`.
===== HandlerFunctions
Incoming HTTP requests are handled by a **`HandlerFunction`**, which is essentially a function that
takes a `ServerRequest` and returns a `Mono<ServerResponse>`. The annotation counterpart to a
handler function would be a method with `@RequestMapping`.
`ServerRequest` and `ServerResponse` are immutable interfaces that offer JDK-8 friendly access
to the underlying HTTP messages. Both are fully reactive by
building on top of Reactor: the request expose the body as `Flux` or `Mono`; the response accepts
any http://www.reactive-streams.org[Reactive Streams] `Publisher` as body.
`ServerRequest` gives access to various HTTP request elements:
the method, URI, query parameters, and -- through the separate `ServerRequest.Headers` interface
-- the headers. Access to the body is provided through the `body` methods. For instance, this is
how to extract the request body into a `Mono<String>`:
Mono<String> string = request.bodyToMono(String.class);
And here is how to extract the body into a `Flux`, where `Person` is a class that can be
deserialised from the contents of the body (i.e. `Person` is supported by Jackson if the body
contains JSON, or JAXB if XML).
Flux<Person> people = request.bodyToFlux(Person.class);
The two methods above (`bodyToMono` and `bodyToFlux`) are, in fact, convenience methods that use the
generic `ServerRequest.body(BodyExtractor)` method. `BodyExtractor` is
a functional strategy interface that allows you to write your own extraction logic, but common
`BodyExtractor` instances can be found in the `BodyExtractors` utility class. So, the above
examples can be replaced with:
Mono<String> string = request.body(BodyExtractors.toMono(String.class);
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class);
Similarly, `ServerResponse` provides access to the HTTP response. Since it is immutable, you create
a `ServerResponse` with a builder. The builder allows you to set the response status, add response
headers, and provide a body. For instance, this is how to create a response with a 200 OK status,
a JSON content-type, and a body:
Mono<Person> person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);
And here is how to build a response with a 201 Created status, Location header, and empty body:
URI location = ...
ServerResponse.created(location).build();
Putting these together allows us to create a `HandlerFunction`. For instance, here is an example
of a simple "Hello World" handler lambda, that returns a response with a 200 status and a body
based on a String:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
HandlerFunction<ServerResponse> helloWorld =
request -> ServerResponse.ok().body(fromObject("Hello World"));
----
Writing handler functions as lambda's, as we do above, is convenient, but perhaps lacks in
readability and becomes less maintainable when dealing with multiple functions. Therefore, it is
recommended to group related handler functions into a handler or controller class. For example,
here is a class that exposes a reactive `Person` repository:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
public class PersonHandler {
private final PersonRepository repository;
public PersonHandler(PersonRepository repository) {
this.repository = repository;
}
public Mono<ServerResponse> listPeople(ServerRequest request) { // <1>
Flux<Person> people = repository.allPeople();
return ServerResponse.ok().contentType(APPLICATION_JSON).body(people, Person.class);
}
public Mono<ServerResponse> createPerson(ServerRequest request) { // <2>
Mono<Person> person = request.bodyToMono(Person.class);
return ServerResponse.ok().build(repository.savePerson(person));
}
public Mono<ServerResponse> getPerson(ServerRequest request) { // <3>
int personId = Integer.valueOf(request.pathVariable("id"));
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
Mono<Person> personMono = this.repository.getPerson(personId);
return personMono
.then(person -> ServerResponse.ok().contentType(APPLICATION_JSON).body(fromObject(person)))
.otherwiseIfEmpty(notFound);
}
}
----
<1> `listPeople` is a handler function that returns all `Person` objects found in the repository as
JSON.
<2> `createPerson` is a handler function that stores a new `Person` contained in the request body.
Note that `PersonRepository.savePerson(Person)` returns `Mono<Void>`: an empty Mono that emits
a completion signal when the person has been read from the request and stored. So we use the
`build(Publisher<Void>)` method to send a response when that completion signal is received, i.e.
when the `Person` has been saved.
<3> `getPerson` is a handler function that returns a single person, identified via the path
variable `id`. We retrieve that `Person` via the repository, and create a JSON response if it is
found. If it is not found, we use `otherwiseIfEmpty(Mono<T>)` to return a 404 Not Found response.
===== RouterFunctions
Incoming requests are routed to handler functions with a **`RouterFunction`**, which is a function
that takes a `ServerRequest`, and returns a `Mono<HandlerFunction>`. If a request matches a
particular route, a handler function is returned; otherwise it returns an empty `Mono`. The
`RouterFunction` has a similar purpose as the `@RequestMapping` annotation in `@Controller` classes.
Typically, you do not write router functions yourself, but rather use
`RouterFunctions.route(RequestPredicate, HandlerFunction)` to
create one using a request predicate and handler function. If the predicate applies, the request is
routed to the given handler function; otherwise no routing is performed, resulting in a
404 Not Found response.
Though you can write your own `RequestPredicate`, you do not have to: the `RequestPredicates`
utility class offers commonly used predicates, such matching based on path, HTTP method,
content-type, etc.
Using `route`, we can route to our "Hello World" handler function:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
RouterFunction<ServerResponse> helloWorldRoute =
RouterFunctions.route(RequestPredicates.path("/hello-world"),
request -> Response.ok().body(fromObject("Hello World")));
----
Two router functions can be composed into a new router function that routes to either handler
function: if the predicate of the first route does not match, the second is evaluated.
Composed router functions are evaluated in order, so it makes sense to put specific functions
before generic ones.
You can compose two router functions by calling `RouterFunction.and(RouterFunction)`, or by calling
`RouterFunction.andRoute(RequestPredicate, HandlerFunction)`, which is a convenient combination
of `RouterFunction.and()` with `RouterFunctions.route()`.
Given the `PersonHandler` we showed above, we can now define a router function that routes to the
respective handler functions.
We use https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html[method-references]
to refer to the handler functions:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
PersonRepository repository = ...
PersonHandler handler = new PersonHandler(repository);
RouterFunction<ServerResponse> personRoute =
route(GET("/person/{id}").and(accept(APPLICATION_JSON)), handler::getPerson)
.andRoute(GET("/person").and(accept(APPLICATION_JSON)), handler::listPeople)
.andRoute(POST("/person").and(contentType(APPLICATION_JSON)), handler::createPerson);
----
Besides router functions, you can also compose request predicates, by calling
`RequestPredicate.and(RequestPredicate)` or `RequestPredicate.or(RequestPredicate)`.
These work as expected: for `and` the resulting predicate matches if *both* given predicates match;
`or` matches if *either* predicate does.
Most of the predicates found in `RequestPredicates` are compositions.
For instance, `RequestPredicates.GET(String)` is a composition of
`RequestPredicates.method(HttpMethod)` and `RequestPredicates.path(String)`.
====== Running a Server
Now there is just one piece of the puzzle missing: running a router function in an HTTP server.
You can convert a router function into a `HttpHandler` by using
`RouterFunctions.toHttpHandler(RouterFunction)`.
The `HttpHandler` allows you to run on a wide variety of reactive runtimes: Reactor Netty,
RxNetty, Servlet 3.1+, and Undertow.
Here is how we run a router function in Reactor Netty, for instance:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
RouterFunction<ServerResponse> route = ...
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
HttpServer server = HttpServer.create(HOST, PORT);
server.newHandler(adapter).block();
----
For Tomcat it looks like this:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
RouterFunction<ServerResponse> route = ...
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
HttpServlet servlet = new ServletHttpHandlerAdapter(httpHandler);
Tomcat server = new Tomcat();
Context rootContext = server.addContext("", System.getProperty("java.io.tmpdir"));
Tomcat.addServlet(rootContext, "servlet", servlet);
rootContext.addServletMapping("/", "servlet");
tomcatServer.start();
----
TODO: DispatcherHandler
===== HandlerFilterFunction
Routes mapped by a router function can be filtered by calling
`RouterFunction.filter(HandlerFilterFunction)`, where `HandlerFilterFunction` is essentially a
function that takes a `ServerRequest` and `HandlerFunction`, and returns a `ServerResponse`.
The handler function parameter represents the next element in the chain: this is typically the
`HandlerFunction` that is routed to, but can also be another `FilterFunction` if multiple filters
are applied.
With annotations, similar functionality can be achieved using `@ControllerAdvice` and/or a `ServletFilter`.
Let's add a simple security filter to our route, assuming that we have a `SecurityManager` that
can determine whether a particular path is allowed:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
SecurityManager securityManager = ...
RouterFunction<ServerResponse> route = ...
RouterFunction<ServerResponse> filteredRoute =
route.filter(request, next) -> {
if (securityManager.allowAccessTo(request.path())) {
return next.handle(request);
}
else {
return ServerResponse.status(UNAUTHORIZED).build();
}
});
----
You can see in this example that invoking the `next.handle(ServerRequest)` is optional: we only
allow the handler function to be executed when access is allowed.

View File

@@ -0,0 +1,393 @@
[[web-reactive]]
= WebFlux framework
This section provides basic information on the reactive programming
support for Web applications in Spring Framework 5.
[[web-reactive-intro]]
== Introduction
[[web-reactive-programming]]
=== What is Reactive Programming?
In plain terms reactive programming is about non-blocking applications that are asynchronous
and event-driven and require a small number of threads to scale vertically (i.e. within the
JVM) rather than horizontally (i.e. through clustering).
A key aspect of reactive applications is the concept of backpressure which is
a mechanism to ensure producers don't overwhelm consumers. For example in a pipeline
of reactive components extending from the database to the HTTP response when the
HTTP connection is too slow the data repository can also slow down or stop completely
until network capacity frees up.
Reactive programming also leads to a major shift from imperative to declarative async
composition of logic. It is comparable to writing blocking code vs using the
`CompletableFuture` from Java 8 to compose follow-up actions via lambda expressions.
For a longer introduction check the blog series
https://spring.io/blog/2016/06/07/notes-on-reactive-programming-part-i-the-reactive-landscape["Notes on Reactive Programming"]
by Dave Syer.
[[web-reactive-api]]
=== Reactive API and Building Blocks
Spring Framework 5 embraces
https://github.com/reactive-streams/reactive-streams-jvm#reactive-streams[Reactive Streams]
as the contract for communicating backpressure across async components and
libraries. Reactive Streams is a specification created through industry collaboration that
has also been adopted in Java 9 as `java.util.concurrent.Flow`.
The Spring Framework uses https://projectreactor.io/[Reactor] internally for its own
reactive support. Reactor is a Reactive Streams implementation that further extends the
basic Reactive Streams `Publisher` contract with the `Flux` and `Mono` composable API
types to provide declarative operations on data sequences of `0..N` and `0..1`.
The Spring Framework exposes `Flux` and `Mono` in many of its own reactive APIs.
At the application level however, as always, Spring provides choice and fully supports
the use of RxJava. For more on reactive types check the post
https://spring.io/blog/2016/04/19/understanding-reactive-types["Understanding Reactive Types"]
by Sebastien Deleuze.
[[web-reactive-feature-overview]]
== Spring WebFlux Module
Spring Framework 5 includes a new `spring-webflux` module. The module contains support
for reactive HTTP and WebSocket clients as well as for reactive server web applications
including REST, HTML browser, and WebSocket style interactions.
[[web-reactive-server]]
=== Server Side
On the server-side WebFlux supports 2 distinct programming models:
* Annotation-based with `@Controller` and the other annotations supported also with Spring MVC
* Functional, Java 8 lambda style routing and handling
Both programming models are executed on the same reactive foundation that adapts
non-blocking HTTP runtimes to the Reactive Streams API. The diagram
below shows the server-side stack including traditional, Servlet-based
Spring MVC on the left from the `spring-webmvc` module and also the
reactive stack on the right from the `spring-webflux` module.
image::images/webflux-overview.png[width=720]
WebFlux can run on Servlet containers with support for the
Servlet 3.1 Non-Blocking IO API as well as on other async runtimes such as
Netty and Undertow. Each runtime is adapted to a reactive
`ServerHttpRequest` and `ServerHttpResponse` exposing the body of the
request and response as `Flux<DataBuffer>`, rather than
`InputStream` and `OutputStream`, with reactive backpressure.
REST-style JSON and XML serialization and deserialization is supported on top
as a `Flux<Object>`, and so is HTML view rendering and Server-Sent Events.
[[web-reactive-server-annotation]]
==== Annotation-based Programming Model
The same `@Controller` programming model and the same annotations used in Spring MVC
are also supported in WebFlux. The main difference is that the underlying core,
framework contracts -- i.e. `HandlerMapping`, `HandlerAdapter`, are
non-blocking and operate on the reactive `ServerHttpRequest` and `ServerHttpResponse`
rather than on the `HttpServletRequest` and `HttpServletResponse`.
Below is an example with a reactive controller:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@RestController
public class PersonController {
private final PersonRepository repository;
public PersonController(PersonRepository repository) {
this.repository = repository;
}
@PostMapping("/person")
Mono<Void> create(@RequestBody Publisher<Person> personStream) {
return this.repository.save(personStream).then();
}
@GetMapping("/person")
Flux<Person> list() {
return this.repository.findAll();
}
@GetMapping("/person/{id}")
Mono<Person> findById(@PathVariable String id) {
return this.repository.findOne(id);
}
}
----
[[web-reactive-server-functional]]
==== Functional Programming Model
include::web-flux-functional.adoc[leveloffset=+1]
[[web-reactive-client]]
=== Client Side
WebFlux includes a functional, reactive `WebClient` that offers a fully
non-blocking and reactive alternative to the `RestTemplate`. It exposes network
input and output as a reactive `ClientHttpRequest` and `ClientHttpResponse` where
the body of the request and response is a `Flux<DataBuffer>` rather than an
`InputStream` and `OutputStream`. In addition it supports the same reactive JSON, XML,
and SSE serialization mechanism as on the server side so you can work with typed objects.
Below is an example of using the `WebClient` which requires a `ClientHttpConnector`
implementation to plug in a specific HTTP client such as Reactor Netty:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
WebClient client = WebClient.create("http://example.com");
Mono<Account> account = client.get()
.url("/accounts/{id}", 1L)
.accept(APPLICATION_JSON)
.exchange(request)
.then(response -> response.bodyToMono(Account.class));
----
[NOTE]
====
The `AsyncRestTemplate` also supports non-blocking interactions. The main difference
is it can't support non-blocking streaming, like for example
https://dev.twitter.com/streaming/overview[Twitter one], because fundamentally it's
still based and relies on `InputStream` and `OutputStream`.
====
[[web-reactive-http-body]]
=== Request and Response Body Conversion
The `spring-core` module provides reactive `Encoder` and `Decoder` contracts
that enable the serialization of a `Flux` of bytes to and from typed objects.
The `spring-web` module adds JSON (Jackson) and XML (JAXB) implementations for use in
web applications as well as others for SSE streaming and zero-copy file transfer.
The following Reactive APIs are supported:
* Reactor 3.x is supported out of the box
* RxJava 2.x is supported when `io.reactivex.rxjava2:rxjava` dependency is on the classpath
* RxJava 1.x is supported when both `io.reactivex:rxjava` and `io.reactivex:rxjava-reactive-streams` (https://github.com/ReactiveX/RxJavaReactiveStreams[adapter between RxJava and Reactive Streams]) dependencies are on the classpath
For example the request body can be one of the following way and it will be decoded
automatically in both the annotation and the functional programming models:
* `Account account` -- the account is deserialized without blocking before the controller is invoked.
* `Mono<Account> account` -- the controller can use the `Mono` to declare logic to be executed after the account is deserialized.
* `Single<Account> account` -- same as with `Mono` but using RxJava
* `Flux<Account> accounts` -- input streaming scenario.
* `Observable<Account> accounts` -- input streaming with RxJava.
The response body can be one of the following:
* `Mono<Account>` -- serialize without blocking the given Account when the `Mono` completes.
* `Single<Account>` -- same but using RxJava.
* `Flux<Account>` -- streaming scenario, possibly SSE depending on the requested content type.
* `Observable<Account>` -- same but using RxJava `Observable` type.
* `Flowable<Account>` -- same but using RxJava 2 `Flowable` type.
* `Publisher<Account>` or `Flow.Publisher<Account>` -- any type implementing Reactive Streams `Publisher` is supported.
* `Flux<ServerSentEvent>` -- SSE streaming.
* `Mono<Void>` -- request handling completes when the `Mono` completes.
* `Account` -- serialize without blocking the given Account; implies a synchronous, non-blocking controller method.
* `void` -- specific to the annotation-based programming model, request handling completes
when the method returns; implies a synchronous, non-blocking controller method.
When using stream types like `Flux` or `Observable`, the media type specified in the
request/response or at mapping/routing level is used to determine how the data should be serialized
and flushed. For example a REST endpoint that returns a `Flux<Account>` will be serialized by
default as following:
* `application/json`: a `Flux<Account>` is handled as an asynchronous collection and
serialized as a JSON array with an explicit flush when the `complete` event is emitted.
* `application/stream+json`: a `Flux<Account>` will be handled as a stream of `Account` elements
serialized as individual JSON object separated by new lines and explicitly flushed after
each element. The `WebClient` supports JSON stream decoding so this is a good use case
for server to server use case.
* `text/event-stream`: a `Flux<Account>` or `Flux<ServerSentEvent<Account>>` will be handled as
a stream of `Account` or `ServerSentEvent` elements serialized as individual SSE elements
using by default JSON for data encoding and explicit flush after each element. This
is well suited for exposing a stream to browser clients. `WebClient` supports
reading SSE streams as well.
[[web-reactive-websocket-support]]
=== Reactive WebSocket Support
WebFlux includes reactive WebSocket client and server support.
Both client and server are supported on the Java WebSocket API
(JSR-356), Jetty, Undertow, Reactor Netty, and RxNetty.
On the server side, declare a `WebSocketHandlerAdapter` and then simply add
mappings to `WebSocketHandler`-based endpoints:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Bean
public HandlerMapping webSocketMapping() {
Map<String, WebSocketHandler> map = new HashMap<>();
map.put("/foo", new FooWebSocketHandler());
map.put("/bar", new BarWebSocketHandler());
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(map);
return mapping;
}
@Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
}
----
On the client side create a `WebSocketClient` for one of the supported libraries
listed above:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
WebSocketClient client = new ReactorNettyWebSocketClient();
client.execute("ws://localhost:8080/echo"), session -> {... }).blockMillis(5000);
----
[[web-reactive-tests]]
=== Testing
The `spring-test` module includes a `WebTestClient` that can be used to test
WebFlux server endpoints with or without a running server.
Tests without a running server are comparable to `MockMvc` from Spring MVC
where mock request and response are used instead of connecting over the network
using a socket. The `WebTestClient` however can also perform tests against a
running server.
For more see
https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples[sample tests]
in the framework.
[[web-reactive-getting-started]]
== Getting Started
[[web-reactive-getting-started-boot]]
=== Spring Boot Starter
The
https://github.com/bclozel/spring-boot-web-reactive#spring-boot-web-reactive-starter[Spring Boot Web Reactive starter]
available via http://start.spring.io is the fastest way to get started.
It does all that's necessary so you to start writing `@Controller` classes
just like with Spring MVC. Simply go to http://start.spring.io, choose
version 2.0.0.BUILD-SNAPSHOT, and type reactive in the dependencies box.
By default the starter runs with Tomcat but the dependencies can be changed as usual with Spring Boot to switch to a different runtime.
See the
https://github.com/bclozel/spring-boot-web-reactive#spring-boot-web-reactive-starter[starter]
page for more details and instruction
This starter also supports the functional web API and will detect automatically `RouterFunction`
beans. Your Spring Boot WebFlux application should use the `RouterFunction` *or* the
`RequestMapping` approach, it is currently not possible to mix them in the same application.
[[web-reactive-getting-started-manual]]
=== Manual Bootstrapping
This section outlines the steps to get up and running manually.
For dependencies start with `spring-webflux` and `spring-context`.
Then add `jackson-databind` and `io.netty:netty-buffer`
(temporarily see https://jira.spring.io/browse/SPR-14528[SPR-14528]) for JSON support.
Lastly add the dependencies for one of the supported runtimes:
* Tomcat -- `org.apache.tomcat.embed:tomcat-embed-core`
* Jetty -- `org.eclipse.jetty:jetty-server` and `org.eclipse.jetty:jetty-servlet`
* Reactor Netty -- `io.projectreactor.ipc:reactor-netty`
* RxNetty -- `io.reactivex:rxnetty-common` and `io.reactivex:rxnetty-http`
* Undertow -- `io.undertow:undertow-core`
For the **annotation-based programming model** bootstrap with:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
ApplicationContext context = new AnnotationConfigApplicationContext(DelegatingWebFluxConfiguration.class); // (1)
HttpHandler handler = DispatcherHandler.toHttpHandler(context); // (2)
----
The above loads default Spring Web framework configuration (1), then creates a
`DispatcherHandler`, the main class driving request processing (2), and adapts
it to `HttpHandler` -- the lowest level Spring abstraction for reactive HTTP request handling.
For the **functional programming model** bootstrap as follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
ApplicationContext context = new AnnotationConfigApplicationContext(); // (1)
context.registerBean(FooBean.class, () -> new FooBeanImpl()); // (2)
context.registerBean(BarBean.class); // (3)
HttpHandler handler = WebHttpHandlerBuilder
.webHandler(RouterFunctions.toHttpHandler(...))
.applicationContext(context)
.build(); // (4)
----
The above creates an `AnnotationConfigApplicationContext` instance (1) that can take advantage
of the new functional bean registration API (2) to register beans using a Java 8 `Supplier`
or just by specifying its class (3). The `HttpHandler` is created using `WebHttpHandlerBuilder` (4).
The `HttpHandler` can then be installed in one of the supported runtimes:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
// Tomcat and Jetty (also see notes below)
HttpServlet servlet = new ServletHttpHandlerAdapter(handler);
...
// Reactor Netty
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create(host, port).newHandler(adapter).block();
// RxNetty
RxNettyHttpHandlerAdapter adapter = new RxNettyHttpHandlerAdapter(handler);
HttpServer server = HttpServer.newServer(new InetSocketAddress(host, port));
server.startAndAwait(adapter);
// Undertow
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();
----
[NOTE]
====
For Servlet containers especially with WAR deployment you can use the
`AbstractAnnotationConfigDispatcherHandlerInitializer` which as a
`WebApplicationInitializer` and is auto-detected by Servlet containers.
It takes care of registering the `ServletHttpHandlerAdapter` as shown above.
You will need to implement one abstract method in order to point to your
Spring configuration.
====
[[web-reactive-getting-started-examples]]
=== Examples
You will find code examples useful to build reactive Web application in the following projects:
* https://github.com/bclozel/spring-boot-web-reactive[Spring Boot Web Reactive Starter]: sources of the reactive starter available at http://start.spring.io
* https://github.com/poutsma/web-function-sample[Functional programming model sample]
* https://github.com/sdeleuze/spring-reactive-playground[Spring Reactive Playground]: playground for most Spring Web reactive features
* https://github.com/reactor/projectreactor.io/tree/spring-functional[Reactor website]: the `spring-functional` branch is a Spring 5 functional, Java 8 lambda-style application
* https://github.com/bclozel/spring-reactive-university[Spring Reactive University session]: live-coded project from https://www.youtube.com/watch?v=Cj4foJzPF80[this Devoxx BE 2106 university talk]
* https://github.com/thymeleaf/thymeleafsandbox-biglist-reactive[Reactive Thymeleaf Sandbox]
* https://github.com/mix-it/mixit/[Mix-it 2017 website]: Kotlin + Reactive + Functional web and bean registration API application
* https://github.com/simonbasle/reactor-by-example[Reactor by example]: code snippets coming from this https://www.infoq.com/articles/reactor-by-example[InfoQ article]
* https://github.com/spring-projects/spring-framework/tree/master/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation[Spring integration tests]: various features tested with Reactor https://projectreactor.io/docs/test/release/api/index.html?reactor/test/StepVerifier.html[`StepVerifier`]

View File

@@ -0,0 +1,249 @@
[[web-integration]]
= Integrating with other web frameworks
[[intro]]
== Introduction
.Spring Web Flow
****
Spring Web Flow (SWF) aims to be the best solution for the management of web application
page flow.
SWF integrates with existing frameworks like Spring MVC and JSF, in both Servlet and
Portlet environments. If you have a business process (or processes) that would benefit
from a conversational model as opposed to a purely request model, then SWF may be the
solution.
SWF allows you to capture logical page flows as self-contained modules that are reusable
in different situations, and as such is ideal for building web application modules that
guide the user through controlled navigations that drive business processes.
For more information about SWF, consult the
http://projects.spring.io/spring-webflow/[Spring Web Flow website].
****
This chapter details Spring's integration with third party web frameworks, such as
http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html[JSF].
One of the core value propositions of the Spring Framework is that of enabling
__choice__. In a general sense, Spring does not force one to use or buy into any
particular architecture, technology, or methodology (although it certainly recommends
some over others). This freedom to pick and choose the architecture, technology, or
methodology that is most relevant to a developer and their development team is
arguably most evident in the web area, where Spring provides its own web framework
(<<mvc,Spring MVC>>), while at the same time providing integration with a number of
popular third party web frameworks. This allows one to continue to leverage any and all
of the skills one may have acquired in a particular web framework such as JSF, while
at the same time being able to enjoy the benefits afforded by Spring in other areas such
as data access, declarative transaction management, and flexible configuration and
application assembly.
Having dispensed with the woolly sales patter (c.f. the previous paragraph), the
remainder of this chapter will concentrate upon the meaty details of integrating your
favorite web framework with Spring. One thing that is often commented upon by developers
coming to Java from other languages is the seeming super-abundance of web frameworks
available in Java. There are indeed a great number of web frameworks in the Java space;
in fact there are far too many to cover with any semblance of detail in a single
chapter. This chapter thus picks four of the more popular web frameworks in Java,
starting with the Spring configuration that is common to all of the supported web
frameworks, and then detailing the specific integration options for each supported web
framework.
[NOTE]
====
Please note that this chapter does not attempt to explain how to use any of the
supported web frameworks. For example, if you want to use JSF for the presentation
layer of your web application, the assumption is that you are already familiar with
JSF itself. If you need further details about any of the supported web frameworks
themselves, please do consult <<web-integration-resources>> at the end of this chapter.
====
[[web-integration-common]]
== Common configuration
Before diving into the integration specifics of each supported web framework, let us
first take a look at the Spring configuration that is __not__ specific to any one web
framework. (This section is equally applicable to Spring's own web framework, Spring
MVC.)
One of the concepts (for want of a better word) espoused by (Spring's) lightweight
application model is that of a layered architecture. Remember that in a 'classic'
layered architecture, the web layer is but one of many layers; it serves as one of the
entry points into a server side application and it delegates to service objects
(facades) defined in a service layer to satisfy business specific (and
presentation-technology agnostic) use cases. In Spring, these service objects, any other
business-specific objects, data access objects, etc. exist in a distinct 'business
context', which contains __no__ web or presentation layer objects (presentation objects
such as Spring MVC controllers are typically configured in a distinct 'presentation
context'). This section details how one configures a Spring container (a
`WebApplicationContext`) that contains all of the 'business beans' in one's application.
On to specifics: all that one need do is to declare a
{api-spring-framework}/web/context/ContextLoaderListener.html[`ContextLoaderListener`]
in the standard Java EE servlet `web.xml` file of one's web application, and add a
`contextConfigLocation`<context-param/> section (in the same file) that defines which
set of Spring XML configuration files to load.
Find below the <listener/> configuration:
[source,xml,indent=0]
[subs="verbatim,quotes"]
----
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
----
Find below the <context-param/> configuration:
[source,xml,indent=0]
[subs="verbatim,quotes"]
----
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
----
If you don't specify the `contextConfigLocation` context parameter, the
`ContextLoaderListener` will look for a file called `/WEB-INF/applicationContext.xml` to
load. Once the context files are loaded, Spring creates a
{api-spring-framework}/web/context/WebApplicationContext.html[`WebApplicationContext`]
object based on the bean definitions and stores it in the `ServletContext` of the web
application.
All Java web frameworks are built on top of the Servlet API, and so one can use the
following code snippet to get access to this 'business context' `ApplicationContext`
created by the `ContextLoaderListener`.
[source,java,indent=0]
[subs="verbatim,quotes"]
----
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
----
The
{api-spring-framework}/web/context/support/WebApplicationContextUtils.html[`WebApplicationContextUtils`]
class is for convenience, so you don't have to remember the name of the `ServletContext`
attribute. Its __getWebApplicationContext()__ method will return `null` if an object
doesn't exist under the `WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE`
key. Rather than risk getting `NullPointerExceptions` in your application, it's better
to use the `getRequiredWebApplicationContext()` method. This method throws an exception
when the `ApplicationContext` is missing.
Once you have a reference to the `WebApplicationContext`, you can retrieve beans by
their name or type. Most developers retrieve beans by name and then cast them to one of
their implemented interfaces.
Fortunately, most of the frameworks in this section have simpler ways of looking up
beans. Not only do they make it easy to get beans from a Spring container, but they also
allow you to use dependency injection on their controllers. Each web framework section
has more detail on its specific integration strategies.
[[jsf]]
== JavaServer Faces 1.2
JavaServer Faces (JSF) is the JCP's standard component-based, event-driven web user
interface framework. As of Java EE 5, it is an official part of the Java EE umbrella.
For a popular JSF runtime as well as for popular JSF component libraries, check out the
http://myfaces.apache.org/[Apache MyFaces project]. The MyFaces project also provides
common JSF extensions such as http://myfaces.apache.org/orchestra/[MyFaces Orchestra]:
a Spring-based JSF extension that provides rich conversation scope support.
[NOTE]
====
Spring Web Flow 2.0 provides rich JSF support through its newly established Spring Faces
module, both for JSF-centric usage (as described in this section) and for Spring-centric
usage (using JSF views within a Spring MVC dispatcher). Check out the
http://projects.spring.io/spring-webflow[Spring Web Flow website] for details!
====
The key element in Spring's JSF integration is the JSF `ELResolver` mechanism.
[[jsf-springbeanfaceselresolver]]
=== SpringBeanFacesELResolver (JSF 1.2+)
`SpringBeanFacesELResolver` is a JSF 1.2 compliant `ELResolver` implementation,
integrating with the standard Unified EL as used by JSF 1.2 and JSP 2.1. Like
`SpringBeanVariableResolver`, it delegates to the Spring's 'business context'
`WebApplicationContext` __first__, then to the default resolver of the underlying JSF
implementation.
Configuration-wise, simply define `SpringBeanFacesELResolver` in your JSF 1.2
__faces-context.xml__ file:
[source,xml,indent=0]
[subs="verbatim,quotes"]
----
<faces-config>
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
...
</application>
</faces-config>
----
[[jsf-facescontextutils]]
=== FacesContextUtils
A custom `VariableResolver` works well when mapping one's properties to beans
in __faces-config.xml__, but at times one may need to grab a bean explicitly. The
{api-spring-framework}/web/jsf/FacesContextUtils.html[`FacesContextUtils`]
class makes this easy. It is similar to `WebApplicationContextUtils`, except that it
takes a `FacesContext` parameter rather than a `ServletContext` parameter.
[source,java,indent=0]
[subs="verbatim,quotes"]
----
ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
----
[[struts]]
== Apache Struts 2.x
Invented by Craig McClanahan, http://struts.apache.org[Struts] is an open source project
hosted by the Apache Software Foundation. At the time, it greatly simplified the
JSP/Servlet programming paradigm and won over many developers who were using proprietary
frameworks. It simplified the programming model, it was open source (and thus free as in
beer), and it had a large community, which allowed the project to grow and become popular
among Java web developers.
Check out the Struts
https://struts.apache.org/release/2.3.x/docs/spring-plugin.html[Spring Plugin] for the
built-in Spring integration shipped with Struts.
[[tapestry]]
== Tapestry 5.x
From the http://tapestry.apache.org/[Tapestry homepage]:
Tapestry is a "__Component oriented framework for creating dynamic, robust,
highly scalable web applications in Java.__"
While Spring has its own <<mvc,powerful web layer>>, there are a number of unique
advantages to building an enterprise Java application using a combination of Tapestry
for the web user interface and the Spring container for the lower layers.
For more information, check out Tapestry's dedicated
https://tapestry.apache.org/integrating-with-spring-framework.html[integration module for
Spring].
[[web-integration-resources]]
== Further Resources
Find below links to further resources about the various web frameworks described in this
chapter.
* The http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html[JSF] homepage
* The http://struts.apache.org/[Struts] homepage
* The http://tapestry.apache.org/[Tapestry] homepage

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff