Project Reactor (Part 3 of 3)
01 November 2020
So far…
In the previous post we introduced Reactor testing and backpressure.
To summarize all three parts:
-
Part 1: Getting started with Reactor: Flux, Mono, zipping, Tuples, and retry
-
Part 2: Backpressure and testing with Reactor
-
Part 3: Spring WebFlux, Netty, and WebClient with a dip into JHipster
Prerequisites
For this post you should have a good understanding of
Java
and Spring Boot
and have read the previous posts about Reactor.
JHipster
JHipster is a code generation tool primarily for building Spring-Boot based projects
with either a React or Angular front end - although it can be extended via blueprints and
there are several (one allows you to create a Vue.js front-end
and one a Micronaut back-end for example).
It also has a feature allowing you to create a Spring Boot WebFlux project.
WebFlux is Spring’s framework for asynchronous, non-blocking web applications - and it works with
Reactive types, especially project Reactor (Flux, Mono, etc.).
To get started, install Node (>10.20.1) and JHipster (latest)
-
Then, from the terminal, make a new directory (named "hip-webflux"), and run jhipster
-
Follow the prompts, and choose monolith and then Reactive (this will create a Spring Boot WebFlux project)
-
For database, you can pick MongoDB, or Cassandra (the reactive generator doesn’t support SQL at the moment)
-
For front end, you can pick React or Angular
-
Pick Maven or Gradle
…This takes a while since it has to load a bunch of dependencies…
Then you should see output like the following:
Server application generated successfully.
Run your Spring Boot application:
./mvnw
Client application generated successfully.
Start your Webpack development server with:
npm start
> hip@0.0.1-SNAPSHOT cleanup /home/adavis/github/adamldavis/hip-webflux
> rimraf target/classes/static/
INFO! Congratulations, JHipster execution is complete!
JDL
After creating the basic project, let’s add some entities.
One of the cool things about JHipster is JDL (Jhipster Domain Language).
Its a DSL for creating projects and entities.
Create a file named "hip.jdl" and put in the following:
entity Person {
name String required
age Integer min(0) max(142)
}
(Feel free to mess around with your JDL and make it more complicated, but for this example, let’s keep it simple)
Import the JDL by running jhipster import-jdl hip.jdl
Now open the project with your favorite IDE (like IntelliJ IDEA for example) and take
a look at the PersonController.
JHipster automatically creates an AuditEvent database for auditing (create, update, delete record events).
Open up the file named "AuditResource" under the "com.mycompany.myapp.web.rest" package (Under src/main/java/).
It should have a "getAll" method that looks something like this:
@GetMapping
public Mono<ResponseEntity<Flux<AuditEvent>>> getAll(ServerHttpRequest request, Pageable pageable) {
return auditEventService.count()
.map(total -> new PageImpl<>(new ArrayList<>(), pageable, total))
.map(page -> PaginationUtil.generatePaginationHttpHeaders(UriComponentsBuilder.fromHttpRequest(request), page))
.map(headers -> ResponseEntity.ok().headers(headers).body(auditEventService.findAll(pageable)));
}
WebFlux allows you to return Reactor types like Mono and Flux from controller methods
and implements a reactive, asynchronous web server using Netty by default.
A typical web server uses a large number of Threads and handles each request in one thread.
The problem with this is Threads in Java are kind of heavyweight
(this should be fixed by project
Loom and virtual threads in Java 15+).
This allows a single Thread to block without holding back the entire application;
however, the number of concurrent requests you can handle is limited by the number of threads
your server can support.
Netty instead uses an EventLoop
per Thread for I/O.
WebFlux enables and simplifies the Java API using Reactive Streams (of which Reactor is one implementation).
No Blocking!
Since the threads in WebFlux are meant to be non-blocking, you must be careful not to block them.
For example, don’t call block()
or Thread.sleep
.
There’s a project called BlockHound to help enforce this
rule throughout your application.
(This doesn’t mean no threads can block; only blocking should happen in dedicated threads)
To implement this, your application should be reactive "all the way down".
So if you have a database, reading from it should also be reactive and non-blocking.
Projects like r2dbc and Spring spring-data-r2dbc
help, among others.
WebClient
WebFlux also includes
WebClient
which is a reactive replacement for RestTemplate.
It makes asynchronous, non-blocking HTTP requests.
You can create an instance of WebClient using the builder pattern starting from the static WebClient.builder() method.
For example:
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
// later on...
WebClient myWebClient = WebClient.builder()
.baseUrl("http://localhost:8080/api-to-call/")
.defaultCookie("cookieKey", "cookieValue")
.defaultHeader(HttpHeaders.CONTENT_TYPE,
MediaType.APPLICATION_JSON_VALUE)
.build();
This builds a WebClient with given baseUrl.
It also provides a cookie and header to use for every request.
There are many more methods to configure the WebClient.
Each request starts with defining the HTTP method,
then you can specify an additional URL path (with or without path variables) and call
exchange which returns a Mono<ClientResponse>.
For example, to get a person from some /v1/persons
REST api:
// get the Course with ID=1 and print it out:
myWebClient.get()
.uri("/v1/persons/{id}", 1L)
.exchange()
.flatMap((ClientResponse response) ->
response.bodyToMono(Person.class))
.subscribe(person -> System.out.println("person = " + person));
The WebClient thus enables all of your HTTP calls to be reactive.
The Spring team likes WebClient so much, they recommend everyone uses it over RestTemplate.
Schedulers
One important type in Reactor is Schedulers.
Schedulers provides various Scheduler flavors usable by publishOn or subscribeOn
for a Flux or Mono. For example:
mono.publishOn(Schedulers.elastic())
Makes a Mono suitable for running blocking tasks like HTTP calls.
Conclusion
I hope you got something from this three part series.
There’s much much more to learn about Reactor, but I hope this is a good introduction.
Reactive programming in Java isn’t always necessary - it’s for enabling thousands of
transactions a second, so you might not need it - but when any
application becomes popular enough, you tend to need high throughput and performance.
Eventually when Java has virtual threads it will make threads more lightweight, however,
Reactive will still be useful due to its backpressure handling, retry logic,
and other benefits.
-
Part 1: Getting started with Reactor, Flux, Mono, zipping, Tuples, and retry
-
Part 2: Backpressure and testing with Reactor
-
Part 3: Spring WebFlux, Netty, and WebClient (and JHipster)
Project Reactor (Part 2)
29 July 2020
In the previous post we introduced Reactor and covered why reactor is important and some of the basics.
We talked about Flux, Mono, zipping, Tuples, and retry.
However, one of the most important aspects of reactive streams, is the ability to handle backpressure.
Backpressure is a mechanism for explicitly handling the problem of having too many items to process in real time.
Without such a mechanism, you typically see the problem of services timing out and eventually
the user sees either a very slow or unresponsive system.
This is compounded by the fact that users will give up quickly if a system is slow to respond.
Many other techniques for handling concurrency do not have explicit mechanisms for handling backpressure.
Instead, system engineers and architects must hope to scale the system either horizontally
(more) servers, or vertically (a faster CPU/more cores/more memory).
Handling Backpressure in Reactor
Reactor, like all implementations of Reactive Streams, has the ability to handle backpressure.
Use one of the following methods on a Flux (or others not listed) to specify which backpressure strategy you want to use:
-
onBackpressureBuffer(): Buffers all items until they can be handled downstream.
-
onBackpressureBuffer(maxSize): Buffers items up to the given count.
-
onBackpressureBuffer(maxSize, BufferOverflowStrategy): Buffers items up to the given count and allows you to specify the BufferOverflowStrategy, such as onBackpressureBuffer(100, BufferOverflowStrategy.DROP_OLDEST)
-
onBackpressureLatest(): Similar to keeping a buffer of only the last item added. If the downstream does not keep up with upstream, only the latest element will be given downstream.
-
onBackpressureError(): Ends the Flux with an error (calling the downstream Subscriber’s onError) with an IllegalStateException from Exceptions.failWithOverflow() if more items were produced upstream than requested downstream.
-
onBackpressureDrop(): Drops any items produced above what was requested.
-
onBackpressureDrop(Consumer): Drops any items produced above what was requested and calls the given Consumer for each dropped item.
With each of these methods, the strategy only applies when the stream produces items faster than they can be handled.
If that’s not the case, for example with a cold stream, no backpressure strategy is necessary.
Also, keep in mind that Reactor is not magic and some care should be taken when considering backpressure strategies.
For example, if each item in the stream is critical, do not use onBackpressureDrop().
If you use onBackpressureError(), you will cause an Exception to be thrown when there are too many items, so use this with extreme caution.
If you use any backpressure strategy, you should consider writing a test to validate
that your whole system will work as expected.
One way you could do this is to simulate a very slow downstream by using Thread.sleep(1000)
for example.
Luckily, Project Reactor supplies us with some great testing features.
Testing
Automated testing is always a good idea, and it would be nice to have tools to directly test Reactive Streams.
Luckily, Reactor comes with a few elements dedicated to testing which are gathered into their own artifact we already included: reactor-test
.
The two main uses of reactor-test are the following:
StepVerifier
Reactor’s StepVerifier can be used to verify the behavior of a Reactor Publisher (Flux or Mono).
Here’s a simple example of a Junit test utilizing StepVerifier:
@Test
public void testStepVerifier_Mono_error() {
Mono<String> monoError = Mono.error(new RuntimeException("error")); //1
StepVerifier.create(monoError) //2
.expectErrorMessage("error") //3
.verify(); //4
}
-
Create a Mono wrapping a RuntimeException imitating an actual error state.
-
Create a StepVerifier wrapping that Mono.
-
Declare that an onError event is expected and the Exception’s error message is “error”.
-
We call verify() at the end. This will throw an AssertionError if any expectations are not met.
Next, we’ll create a Mono of just one string and verify it:
@Test public void testStepVerifier_Mono_foo() {
Mono<String> foo = Mono.just(“foo”); //1
StepVerifier.create(foo) //2
.expectNext(“foo”) //3
.verifyComplete(); //4
}
-
Create a Mono wrapping one value, “foo”.
-
Create a StepVerifier wrapping that Mono.
-
Expect onNext is called with “foo”.
-
Call verifyComplete() has the same effect as verify() but also expects onComplete was called.
Verifying Backpressure
Next we’ll test a Flux with four values and verify any additional values are dropped using backpressure handling.
@Test
public void testStepVerifier_Flux_backpressure() {
Flux<Integer> source = Flux.<Integer>create(emitter -> { //1
emitter.next(1);
emitter.next(2);
emitter.next(3);
emitter.next(4);
emitter.complete();
}).onBackpressureDrop(); //2
StepVerifier.withVirtualTime(() -> source, 3) //3
.expectNext(1)
.expectNext(2)
.expectNext(3)
.expectComplete() //4
.verifyThenAssertThat()
.tookLessThan(Duration.ofMillis(50)); //5
}
-
Create a Flux of just four numbers using an emitter (this is a "hot" flux meaning it is time sensitive).
-
Using onBackpressureDrop
to drop any items that aren’t handled fast enough.
-
Create a StepVerifier with the Flux using withVirtualTime
and requesting only 3 items. Then call expectNext for each value expected.
-
Then call expectComplete
thus verifying that the backpressure logic worked!
-
Finally, we verify that the Flux is complete and the whole process took less than a 50 milliseconds.
The main point of using withVirtualTime
here is to specify that we only request three elements.
This emulates the real-world experience that would happen if a down-stream (Subscriber) could not keep up
with the upstream (Publisher), since the down-stream is what causes items to be requested in Reactive Streams.
Although this example only uses 1,2,3,4 you can imagine
the stream (Flux) could be composed of any objects.
Virtual time (with StepVerifier) can also be useful for testing things like the Flux.interval
.
TestPublisher
The TestPublisher<T>
class offers the ability to provide
finely tuned data for test purposes.
TestPublisher<T>
is a reactive-streams Publisher<T> but can be converted to
either a Flux or Mono.
TextPublisher has the following methods:
-
next(T) and next(T, T…) : Triggers 1-n onNext signals.
-
emit(T…) : Does the same as next and terminates with an onComplete signal.
-
complete() : Terminates with an onComplete signal.
-
error(Throwable) : Terminates with an onError signal.
The following demonstrates how you might use TestPublisher<T>
:
TestPublisher<Object> publisher = TestPublisher.create(); //1
Flux<Object> stringFlux = publisher.flux(); //2
List list = new ArrayList(); //3
stringFlux.subscribe(next -> list.add(next),
ex -> ex.printStackTrace()); //4
publisher.emit("foo", "bar"); //5
assertEquals(2, list.size()); //6
assertEquals("foo", list.get(0));
assertEquals("bar", list.get(1));
-
Create the TestPublisher instance.
-
Convert it to a Flux.
-
Create a new List. For test purposes we will use this list to collect values from the publisher.
-
Subscribe to the publisher using two lambda expressions for onNext and onError. This will add each value emitted from the publisher to the list.
-
Finally, emit the values “foo” and “bar” from the TestPublisher.
-
Assert the list’s size is two as expected.
Note that you must subscribe to the TestPublisher (which is done by subscribing to the stringFlux in the above example) before emitting any values.
Coming Next…
In this article I’ve shown how Reactor can be used to handle backpressure and how to test it.
In my next article, we’ll look into how Reactor integrates with the whole Spring ecosystem - especially with WebFlux and WebClient.
Project Reactor (Part 1)
07 July 2020
What is Reactor?
The purpose of Reactor,
and reactive steams in general is to enable operations on large amounts of data
to be broken down and executed on many different threads (multi-threading)
in the most efficient, scalable, and fast way possible.
Although parallel processing can be achieved simply using Java 8’s parallel stream, reactive streams add a plethora of additional functionality and customization such as error handling, retry, caching and replaying streams, handling backpressure, and more.
You can think of a reactive stream as having three rails, the data rail, the completion rail (whether or not the stream has completed), and the error rail. Also, each of the rails can be converted into the other: complete streams could be replaced,
an operation could throw an Exception, or an Exception could be handled and replaced with more data.
The core types of Reactor are Flux (a stream of zero to any number of items) and
Mono (a stream of zero or one item).
Getting Started
If you have a Maven build, add the following to your pom file:
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.3.5.RELEASE</version>
<scope>test</scope>
</dependency>
For Gradle builds, add the following to your Gradle build file’s dependencies:
implementation 'io.projectreactor:reactor-core:3.3.5.RELEASE'
testImplementation 'io.projectreactor:reactor-test:3.3.5.RELEASE'
Creating a Flux or Mono
You can create a Flux from fixed data (cold) or programmatically from dynamic data (hot).
The following are some different way to create a cold Flux:
Flux<String> flux1 = Flux.just("a", "b", "foobar"); //1
List<String> iterable = Arrays.asList("a", "b", "foobar");
Flux<String> flux2 = Flux.fromIterable(iterable); //2
Flux<Integer> numbers = Flux.range(1, 64); //3
-
Create a Flux from a list of values.
-
Create a Flux from an iterable.
-
Create a range from 1 to 64.
You can create a simple Mono that is empty or has just one element like the following:
Mono<String> noData = Mono.empty(); //1
Mono<String> data = Mono.just("foo"); //2
Mono<String> monoError = Mono.error(new RuntimeException("error")); //3
-
Create an empty Mono.
-
Create a Mono with one element.
-
Create a Mono wrapping a RuntimeException.
You can also programmatically create a (hot) Flux using one of the generate, create, or push methods.
Tuples and Zip
Tuples are strongly typed collections of two or more elements and Reactor comes with them built in. Some operations such as zipWith, return reactive streams of Tuples.
Flux has an instance method zipWith(Publisher<? extends T2> source2)
which has a return type of Flux<Tuple2<T,T2>>
. It waits for both Fluxes (the initial flux and source2) to emit an element and then combines the two into a Tuple. There’s also static method Flux.zip which is overloaded to take from two to eight Publishers and zip them together.
Zipping is useful when you want to perform multiple operations that return reactive results (Flux or Mono).
Mono has two main flavors of zipping (which both have a return type of Mono<Tuple2<T,T2>>
):
-
zipWith(Mono<? extends T2> other) – Zips the current stream with another stream, giving the combination of each corresponding element in the form of Tuple2.
-
zipWhen(Function<T,Mono<? extends T2>> rightGenerator) – Zips the current Mono with another Mono, giving the combination of each corresponding element in the form of Tuple2, but only after the first stream’s operation has completed.
For example, given you have two methods which perform asynchronous operations Mono<Course> getCourse(Long id)
and Mono<Integer> getStudentCount(Course course), imagine you want to get the student count from the course Id you could do the following:
Mono<Integer> getStudentCount(Long id) {
return getCourse(id)
.zipWhen(course -> getStudentCount(course))
.map(tuple2 -> tuple2.getT2());
}
This is a simple example but you can imagine combining two different entities, or performing logic on them before returning, or calling another method which takes two arguments, and so on.
Reactor Addons
Project Reactor provides additional functionality under the io.projectreactor.addons groupId. Reactor extra includes additional math functions, different ways to retry including Jitter and Backoff, and TupleUtils.
<dependency>
<groupId>io.projectreactor.addons</groupId>
<artifactId>reactor-extra</artifactId>
<version>3.3.3.RELEASE</version>
</dependency>
For Gradle builds, add the following to your Gradle build file’s dependencies:
implementation 'io.projectreactor.addons:reactor-extra:3.3.3.RELEASE'
When your application fails at an integration point, such as when calling another RESTful service, to make your overall system reliable you might want to retry the call some number of times. However, to keep from overloading the failing service, you should employ Backoff, or increasing the time between each retry, and Jitter, randomly modifying the time so that the retries from many different instances do not happen at the same time (correlate). For example take a look at the following code:
var retry = Retry.anyOf(IOException.class) \\1
.exponentialBackoff(Duration.ofMillis(100), \\2
Duration.ofSeconds(60))
.jitter(Jitter.random()) \\3
.retryMax(5)
.withApplicationContext(appContext) \\4
.doOnRetry(context ->
context.applicationContext().rollback());
return flux.retryWhen(retry); \\5
-
We create the Retry with exception value of IOException, meaning it will retry only when that exception is thrown.
-
We define exponential backoff with a starting value of 100 ms and maximum value of 60 seconds.
-
We add random Jitter and set the retry max to 5, meaning it retry at most 5 times.
-
We add the Spring ApplicationContext and use it to apply rollback after each failure.
-
Finally we call retryWhen(retry) on a Flux instance to apply the Retry to that Flux.
Spring Handbook
01 March 2020
It’s been a while since I’ve posted. There’s been a lot going on (I’ve been very busy).
I started a new position at a large corporation here near Orlando (a mouse-themed amusement park; more on this later).
Meanwhile, I’ve finally started work on "Spring Handbook".
Spring Handbook
I’ve been using Spring for a long, long time (since 2005).
It’s been there throughout almost my entire Java development career and it’s evolved substantially over the years.
I’ve had "Spring Handbook" on my check-list of books to write for a long time.
It’s going to be picked up by Apress, and is currently under development.
(*Edit\* It’s published!)
I’m really excited about this one - there is so much to cover and it’s so relevant to software development today especially with Java.
I’m going to cover so many things:
-
Core Spring
-
Spring Data
-
Spring Security
-
Spring Boot
-
Webflux and Reactor
-
and tons more…
GraphQL
In other news, I’ve learned a lot about GraphQL at my new job.
It’s combines a typed schema with the ability for clients to tell the server exactly what they want.
This reduces the amount of unnecessary data requested, the number of HTTP requests needed, thereby increasing performance overall of a software system.
Although I was suspect at first, it’s actually very clever. I’ve learned a lot about it and hope that I can share more about it later on.
Groovy 3 Course and Gradle
30 September 2019
As I’ve mentioned I’ve been working on courses on Leanpub.
I’m happy to announce that the first one is complete!
It has changed a lot since my initial thought process.
Among other things, it has a new subtitle,
"Go from beginner to expert Groovy Programmer with Groovy 3.0 and Gradle 5".
I decided that it needed more specifics than "just Groovy" and Gradle
is a popular build tool and a good reason to start learning Groovy.
Check it out: "Groovy 3 Course".
In case you’re curious, this course contains tons of new material
and is not simply a copy of Learning Groovy 3 - far from it.
It could be seen as a companion but covers more of some topics and less of others.
I’ve also decided to launch it with a very low introduction price.
I want the barrier to entry to be very low.
Please share it someone you think might benefit from it if not yourself.
As always, all feedback is welcome (adamd @ this website).
Let me know what you’d like to see more about or less about.
Due to other projects I’m working on, I’m going to postpone for now
working on "Advanced Groovy"
(I hope to cover not just advanced Groovy but also some other projects
in greater detail).
I have a lot going on (including yet another second edition book) but I hope
to get to it eventually.
Learning Groovy 3 Released
05 August 2019
The second edition of "Learning Groovy" (which covers Groovy 3.0 thus the name) is now available. After months of researching, writing, and editing, it has finally gone to print! I’m really excited to announce this and I hope it spreads the love of Groovy far and wide.
Groovy can be used as a dynamic or static language depending on your choice, however it’s hard to condense that into a short subtitle. We also wanted to include how it is closely related to Java, hence the subtitle, "Java-Based Dynamic Scripting."
The twitter announcement is here. And the book itself can be found on Apress or my books page or wherever you prefer to purchase books.
Groovy 3 beta-3 should come out shortly, with release candidates soon after, so this is book is out slightly before the final 3.0 release. However, if you like to stay ahead of the curve, now is a good time.
Gr8Conf EU 2019
28 June 2019
I had the great privilege of leading a workshop and two talks at Gr8Conf EU 2019 in Copenhagen, Denmark. This was also my first time attending Gr8Conf EU (I did attend Gr8Conf US 2017). My life has finally quieted down enough now that I can write about my experience.
The conference was, in a word, great. I saw a few familiar faces, like Paul King, Jeff Scott Brown, Michael Carducci, Sergio del Amo Caballero, and Jeff Beck (all of whom I’ve seen before, mostly as just another audience member), and met some great new people whom I’ve never seen before but who certainly left a lasting impression that I won’t soon forget like Vladimír Oraný, Szymon Stepniak, Jennifer Strater, Charlotte Mays, and others. For an extremely introverted person such as myself, I don’t often talk to so many people at one time, and I’m grateful for the opportunity to converse and openness of everyone at this conference. (The coffee and food was great too!)
There were too many great talks to list, but some highlights for me were Paul King’s keynote on Groovy 2.5, 3 and 4 (which is available online), Jesper Steen Møller’s talk, “Reactive Web APIs with Micronaut” (which brilliantly showed how to use Reactive Streams within a micronaut based microservice including a demo of how to use a backpressure strategy), and the “Running a developer’s blog” talk by Szymon Stepniak (I hope to use some of his suggestions in the future if I ever get the time).
It was a really great conference and Copenhagen is beautiful. I hope to return again next year (or maybe in two years if not next year).
Meanwhile, I’m finished with the final updates to “Learning Groovy 3” which should be released later this year! I worked on this for several months this year and my technical reviewer gave me some great feedback so it should be a good one. In addition to updating the book for Groovy 3.0 and Grails 3.3, I’ve also updated and polished additional areas of the book based on my personal understanding of Groovy and things that have changed over the years.
Micronaut
31 March 2019
Micronaut was built by many of same people behind Grails. It uses compile-time annotation processing to build an application with a deadly fast startup time, has support for reactive streams, netty, and a reactive http client, among other things.
It’s open-source (Apache 2) and supports applications written in Java, Groovy, or Kotlin.
Due to it’s non-reflective nature, it naturally enables applications to run on the GraalVM, which is described:
`"GraalVM is a new universal virtual machine from Oracle that supports a
polyglot runtime environment and the ability to compile Java
applications down to native machine code."` –https://micronaut.io/
GraalVM compiles applications to native code allowing for incredibly fast application start-up speeds even for large applications, which makes it best suited for “serverless” applications, like AWS Lambdas or the like.
Even running on a vanilla JVM, Micronaut has very low overhead (in both memory and processor time) and has profiles for targeting different platforms: server, function (like AWS Lambda), or command-line applications.
It came out not too long ago and I’ve only made a few applications with it but so far it looks really great!
I’ve added an example of using micronaut with groocss and the asset-pipeline. Check it out.
Groovy 2.5 & 3
13 February 2019
If you have followed me for any time, you know I’m a big fan of Groovy, the super-Java-like language that runs on the JVM. You’ve probably heard of it, maybe you even use it, but what is less known is that it’s constantly evolving and has some great new features coming soon (or already here).
Inspired by some recent news, here’s all about Groovy 2.5 and 3.
Updates in Groovy 2.5
Groovy 2.5 added support for JDK9+, added 11 new AST transformations, and added the macro feature which makes writing AST transformations much easier.
The annotations added in Groovy 2.5 include: @AutoFinal, @AutoImplement, @ImmutableBase, @ImmutableOptions, @MapConstructor, @NamedDelegate, @NamedParam, @NamedParams, @NamedVariant, @PropertyOptions, and @VisibilityOptions.
-
@AutoImplement: Automatically implements missing abstract methods (such as those from an Interface). You can specify an Exception to throw from those methods such as UnsupportedOperationException. It can be useful for generating test stubs or when you only need to implement a subset of inherited abstract methods.
-
@AutoFinal: Automatically adds final modifier to method parameters.
-
@MapConstructor: Adds a constructor to your class that has one Map parameter and expects field-names as keys and sets the corresponding field values.
Also many annotations were improved with additional attributes. For example, @TupleConstructor now includes seven more attributes. The @Immutable annotation was updated to recognize the Date/time classes added in Java 8 are immutable, and to handle Optional.
Updates in Groovy 3
Groovy 3 sports a completely rewritten parser that brings Groovy up to parity with the latest Java 11 syntax along with new Groovy-only features. It runs on JDK 8 minimum and has better support for JDK 9/10/11.
The Java-like syntax now includes Java-style lambda expressions and method references, array initialization, and do/while loops, which have eluded Groovy for many years.
Edit: There is hope that in the near future after working out some issues. The new parser also compiles to “indy” by default which uses Java’s invokedynamic feature. This has been available for years, but was not the default before. This, along with other changes, makes Groovy code more performant.
New Operators
Identity: === can now be used to express identity-equal and !== and not identity-equal. Since Groovy interprets == as “.equals”, it used “.is” for identity-equals in the past. The support of “===” should avoid some confusion. This is similar to JavaScript’s === operator.
Negative variants of operators: !instanceof and !in are now supported. This will simplify the syntax in these situations. Before you would have to type !(x instanceof Date) whereas now you can simply type x !instanceof Date.
Elvis Assignment: You may be familiar with the elvis operator (?:) in Groovy. In many cases you would use this operation to provide a default when assigning a value. For example: name = name ?: ‘None’. Now you can shorten this expression to have the same meaning in Groovy 3 with the following: name ?= 'None’
Safe indexing: Much like the safe-reference operator, there is now a safe-indexing operator, ?. This allows you to access an index of an array (or List), but if the array is null, it will return null instead of throwing an exception. For example the following would set the value to the first value of the array, or null if the array is null: value = array?[0]
Java Parity
Groovy 3 support new features added from Java 8 to 11, such as lambda expressions, method references, constructor references, and even local variables (var).
All flavours of lambda expressions are supported (and currently compiled to Closures):
-
No parameters: () → expression
-
Single paramter: x → expression
-
Explicit return is optional: (x, y) → { x * y }
-
Types are allowed: (int x, int y) → { return x + y }
-
Default values are allowed: (int x, int y = 10) → x+y
There’s much more….. in fact, I’m working on updating my book, Learning Groovy, and releasing a second edition later this year, so stay tuned!
Conclusion
As you can see there’s a ton to be excited about with Groovy 3. Groovy 3.0.0-alpha-4 is available right now so go check it out.
Upgrading to Java 11
11 December 2018
So you want to upgrade to Java 11?
Maybe you’ve put off upgrading Java for a while but are realizing that Oracle soon plans to stop supporting Java 8. Have no fear, since you’ve procrastinated, other people have gone through the pain already and shared what they learned!
First of all, what’s new in Java 9, 10, and 11? The big things are JShell, modularity, local variable declarations (var), and in 11 the removal of JavaEE, xml, and corba among other things. Davide Angelocola shared a good summary of the new features, as well as his notes on upgrading to Java 11 here in this tweet (pdf).
In another post by Giacomo Veneri gives a good overview of features from 8 to 11 including creating immutable collections, new String methods, and more and comes with a handy chart.
image
Due to Oracle’s change in licensing it is highly recommended you use OpenJDK in production now (it’s mostly identical to Oracle Java and Oracle now charges for Oracle JDK in production). Other editions of Java exist or will exist in the future such as Amazon Coretto due to the changes.
Test Driven Design (TDD)
27 November 2018
When I first started to learn programming, first on a TI-89 calculator and then QBASIC, and even later in Java, I had no idea what testing was all about. I thought testing was running the program and making sure it worked. Maybe even put some debug print statements here and there. It was years later I learned about automated testing and test frameworks like JUnit.
As the years went by, mainly after starting to work, I learned the benefit of writing tests - not only to ensure the correctness of code, but also to enable future changes without the fear of breaking things. This is the strongest quality of tests: to enable change.
Writing tests also helps you think the problem you’re trying to solve and clarifies your thinking. As you write the test you have to figure out what exactly your code does, what are the edges cases, and what could go wrong. With TDD (Test Driven Development), it simply goes one step further: you write the tests first, then the code. Instead of blindly typing out the code for a solution you vaguely hold in your head, you specify what you want your code to do - the solution space - then go about making it happen - the implementation. It has the added benefit that your code is always well tested, since you wrote the tests first, rather than an after-thought.
The last D in TDD could also stand for “Design” since when you write tests you are often forced to design the code to be more easily testable. This helps keep code clear and concise with a good separation of concerns. You should divide you logic into the smallest units possible and TDD helps you do this.
I don’t always do TDD - many times the code is so simple that writing tests feels like repeating yourself - but when I do I find the resulting code to be clear, correct, and well designed. If you haven’t tried it, I highly suggest trying TDD today.
Beyond Java 9
18 July 2018
I’m at UberConf this week - it’s going great. Last night I sat through a talk by Venkat Subramaniam called “Beyond Java 9″. I’ve always been curious about what’s coming up (and already here in Java 10) and Venkat always is a great speaker so I was excited to go to this one. Here are my notes:
CompleteableFuture was added in Java 8. Many of us missed it because we were so focused on Streams. Java 9 added modules.
Oracle has decided to start releasing a new major version of Java every 6 months.Not so much for the end developers, this is to make adding new features to language easier and less stressful for the teams behind Java.
At the same time, Oracle is changing their support of previous versions to some degree. If you do not pay for service, this doesn’t effect you except that if you want to be using a “supported” version of Java at all times, you will have to upgrade every six months.
If you do pay for standard support, you can get up to three years of support for long-term-service (LTS) versions of which Java 11 is the next one.
The standard reliability of Java is not changing. Despite the appearance of faster releases, the actual development of the Java the language is not getting much faster. By changing to a major release every six months, the Java language features that are in development for years (sometimes more than ten years) don’t have the added pressure of fitting into one release every three or four years. This way each release can add one or two major features, rather than cramming tons of new features into each major release - as has been the case in the past.
Meanwhile, Oracle is continuing to release OpenJDK as open source.
This allows other companies, such as Azul, to modify it and provide their own support contracts, effectively creating a marketplace for JVMs (which is a good thing for many reasons). This allows some competition and is a force towards innovation.
Which comes to one of the most important points I took from this talk:
“Java will not innovate on language features. It will innovate on Implementation.”
Java has never been on the cutting edge of language features, instead the people behind Java take a long look at what works in other languages and takes time to implement these features in the best way possible. For example, Java 8 lambdas expressions use invoke-dynamic. The behind the scenes implementation allows lambdas to be extremely performant and at the same time allow for future improvements.
Venkat then went on to talk about upcoming projects in Java: Project Panama, Project Valhalla, Project Amber, and Project Loom, which I will not go into right now. This talk was both entertaining and informative (Venkat uses metaphors and jokes sprinkled throughout his talks).
Although this is a large change in the Java language development cycle, in the end this is a good thing because it will encourage Java developers to update more often and spread out new features over more time, not bundling them all at once.
Reactive Streams in Java
08 July 2018
So… I’m working on a thing, Reactive Streams in Java. This has been on my back burner for a long time, and I started work on it for real early this year.
Here’s the description from the book’s landing page:
"Reactive Streams are a standard approach to handling concurrency in modern applications. The Java 9 release includes new support for reactive streams. This book attempts to provide an easy introduction to Reactive Streams in Java including in depth introductions to RxJava, Akka Streams, and Reactor and how they can be used."
This book will cover everything you need to know about existing projects and how they translate in Java 9 (and 10 and 11). Mainly in Java 10 they’ve introduced “var” and this can be used for intermediate variables.
This book uses a progressive publishing model. As the book grows, so does the price. This encourages early adopters, so… maybe think about buying it. I’m not expecting a lot of sales so I appreciate every reader. Feel free to ask me questions (or pester me to keep writing).
This book has been picked up by a publisher - I’d rather not say who because I’m not sure I’m allowed to :) - and will be published in a complete, edited, and polished version later this year (winter 2018). However, the only way to get it right now is from leanpub. Thanks!
Modern Java: 2nd Edition
07 December 2017
In case you didn’t know, I’ve been working on
Modern Java: 2nd Edition
for more than a year now. Over that time I’ve decided to
add Java 9 and cover more libraries and frameworks like Hibernate and
RxJava. I’ve also fleshed out some existing sections more fully.
After many distractions and other projects getting in the way,
I finally feel like I’m coming close to finishing it! It’s now
more of a full introduction to Java and the Java ecosystem from
features of Java 8 and 9 all the way to testing, building, web
frameworks, reactive streams, and a lot more.
RxJava
15 November 2017
RxJava is the open-source library for
reactive programming that is part of the ReactiveX project. ReactiveX includes
implementations in several different languages including rxjs, RxRuby, RxSwift,
RxPHP, RxGroovy and many more.
RxJava 2 was rebuilt to be compatible with the Reactive Streams specification and
is preferable to RxJava 1.x since it is scheduled for end-of-life. There were many
changes from version 1 to 2 that could be confusing.
To avoid confusion we will focus on RxJava 2.
Flowable
The basic entry class in RxJava is io.reactivex.Flowable
.
It implements the Reactive-Streams Pattern and offers factory methods,
intermediate operators and the ability to consume reactive dataflows.
The following example demonstrates using RxJava to do a simple calculation on a range of numbers:
public static List doSquares() {
List squares = new ArrayList();
Flowable.range(1, 64) // (1)
.observeOn(Schedulers.computation()) // (2)
.map(v -> v * v) // (3)
.blockingSubscribe(squares::add); // (4)
return squares;
}
-
Create a range from 1 to 64.
-
Call the method observeOn
to determine which Scheduler
to use.
This determines on which Thread the flow will run.
-
The map
method transforms each value. In this case we calculate the square.
-
Finally, we initiate the flow by calling a “subscribe” method.
In this case, blockingSubscribe
blocks until the entire flow has
completed. This means that the squares
list will be populated
before the return statement. Otherwise the flow would run on a
different thread and the values in the squares list would be
unpredictable at any given time.
Gr8Conf US and other things…
05 August 2017
I just back from Gr8Conf in Minneapolis last week and I had a great time. In case you missed it, I led a three hour workshop on Groovy and the slides and code are available (I did some practice screen-casts on youtube as well). Although I was pretty nervous leading up to it, I think it went really well. To those who attended: Thank you for listening and asking such great questions, I hope you
enjoyed it! This was my first such workshop and everyone was super nice, so thank you again. I look forward to doing talks and/or
workshops again in the future.
There were tons of great talks at Gr8Conf and although I didn’t get
to go to all of them (I wish I could) I learned a lot and loved the
energy from the conference in general. The “hallway discussions” and lunches were great as well.
In other news, “Learning Groovy” is now featured on the official
Groovy website,
and was featured in "Groovy Calamari"!
I can’t stress enough how
thrilled I am about this! Meanwhile, thank you to my company the
SDG for supporting me in all these endeavors as well as just being
a great company.
Modern Java: Second Edition
is still in the works
and I’ve expanded it to include Java 9. I’m sorry this one
has been in production for so long. I hope to get it wrapped
up before the final release of Java 9 in September. :-)
There’s been a lot going on in the past year …but I won’t
make excuses. The good thing is the final product is going to
be great. With all the new stuff in Java 9, plus more experience
with Java 8 and other things, it should be interesting.
I’m also mulling over next books, but nothing to announce yet.
I met and talked with tons of people at Gr8Conf, but one in
particular is Eric Helgeson @nulleric. If you’re at all interested
in Grails 3, check out his book, Practical Grails 3.
The cool thing about it is he’s selling his book completely on his own
and the book covers the app he wrote to sell the book.
Also, I’m still working on GrooCSS
and have tons of ideas for it.
Thanks for reading! Hopefully I’ll write again soon…
Groovy News!
28 July 2016
Great news everyone!
I can now announce, Learning Groovy is
available for pre-order on Apress!
They have been great to work with and it is much improved from the original version due to their
feedback and the technical editor.
Modern Java: Second Edition is available in
beta-mode on Leanpub! This book joins together my original
Modern Java book, with all of the stuff on Java 8 and some new stuff will be coming soon!
I’ve launched GrooCSS, a little pet project of mine. More to come on this later. Check it out!
Older posts are available in the archive.