diff --git a/_config.yml b/_config.yml index 64701882..5960360e 100644 --- a/_config.yml +++ b/_config.yml @@ -16,6 +16,8 @@ description: >- # this means to ignore newlines until "baseurl:" At the very least, it's an outlet for writing about tech and things I find interesting! +include: ["_includes"] + github_username: StevenPG linkedin_username: test diff --git a/_drafts/2024-09-17-Java-Streams-Gather-Jdk-23.markdown b/_drafts/2024-09-17-Java-Streams-Gather-Jdk-23.markdown deleted file mode 100644 index 65846c01..00000000 --- a/_drafts/2024-09-17-Java-Streams-Gather-Jdk-23.markdown +++ /dev/null @@ -1,75 +0,0 @@ ---- -layout: post -title: "Jdk 23 Streams - Using Gather" -toc: true -date: 2024-09-17 12:00:00 -0500 -categories: -- software -- java ---- - -# It's finally here! JDK 23 -TODO - make small XXXTODOXXX - -Just kidding, this isn't an LTS release - -The next LTS release that will include all of the changes in JDK 23 is XXXTODOXXX and -that won't be available until XXXTODOXXX - -We can install Java 23 quickly using Sdkman! using `sdk install java 23-tem` - -The link to the release notes for Jdk23 are right here: https://openjdk.org/projects/jdk/23/ - -But this article is going to focus on a pretty powerful addition for anyone that's been -leveraging Java Streams heavily for stream processing! - -Specifically Gatherers, the JEP is right over here: https://openjdk.org/jeps/473 - -## JEP-473 - -Stream Gatherers is a preview API delivered in JDK 22 that's in preview again -in JDK 23. Gatherers allow for stream pipelines to transform -data through intermediate operations instead of needing to perform -a terminating operation and generate a new stream. - -Today, if we want to process a list of elements, then perform an operation on the entire list, -and then continue processing the list, we'd have to do it like this: - -```java -TODO - add an example here -``` -With Gatherers, we're able to do this in a cleaner way by -doing our extra processing in an intermediate method! - -```java -TODO - add an example here -``` - -Here's the full example code (and we don't need a default class anymore with JDK 23!) - -```java -import java.util.function.Predicate; -import java.util.stream.Gatherer; -import java.util.stream.Stream; - -void main(String[] args) { - - var result = Stream.of(1, 2, 3) // The Stream - .filter((integer -> integer > 0)) // Intermediate Operation - // TODO - gather example - .toList(); // The Terminal Operation - System.out.println(result); -} - -``` - -```bash - $ javac demo.java --enable-preview -source 23 - $ java --enable-preview demo -``` - -TODO - find real life example -- distinctBy -- grouping and applying a change to the whole collection - -[soby-chako]: https://github.com/sobychacko diff --git a/_includes/custom_footer.html b/_includes/custom_footer.html new file mode 100644 index 00000000..009b4831 --- /dev/null +++ b/_includes/custom_footer.html @@ -0,0 +1,35 @@ + \ No newline at end of file diff --git a/_layouts/base.html b/_layouts/base.html new file mode 100644 index 00000000..8ee5156c --- /dev/null +++ b/_layouts/base.html @@ -0,0 +1,20 @@ + + + +{%- include head.html -%} + + + +{%- include header.html -%} + +
+
+ {{ content }} +
+
+ +{%- include custom_footer.html -%} + + + + \ No newline at end of file diff --git a/_layouts/default.html b/_layouts/default.html index e0fb2853..651398ce 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -16,7 +16,7 @@
- {%- include footer.html -%} + {%- include custom_footer.html -%}
+ + {%- include custom_footer.html -%} diff --git a/_posts/2024-09-17-Java-Streams-Gather-Jdk-23.markdown b/_posts/2024-09-17-Java-Streams-Gather-Jdk-23.markdown new file mode 100644 index 00000000..34d60a30 --- /dev/null +++ b/_posts/2024-09-17-Java-Streams-Gather-Jdk-23.markdown @@ -0,0 +1,230 @@ +--- +layout: post +title: "Jdk 23 Streams - How To Use Gather Operations" +toc: true +date: 2024-09-29 12:00:00 -0500 +categories: +- software +- java +--- + +# It's finally here! JDK 23 + +Just kidding, this isn't an LTS release + +The next LTS release that will include all of the changes in JDK 23 (assuming Gatherers make the cut) is JDK 25 and +that won't be available until September 2025. + +This post is a simple overview of the new Gatherer and related classes that have been added +as part of JDK 23. These operations are incredibly powerful and right away, I wasn't able to find +a good resource on the web that helped me understand. + +SO, in keeping with this site, I intend to write up a simple article that I (and others!) can reference +in the future when we need to understand something about this new non-terminating stream capability! + +### Let's get to demo-ing! + +We can install Java 23 quickly using Sdkman! using `sdk install java 23-tem` + +The link to the release notes for Jdk23 are right here: [https://openjdk.org/projects/jdk/23][openjdk23] + +This feature is a pretty powerful addition for anyone that's been +leveraging Java Streams heavily for stream processing! + +Specifically for Gatherers, the JEP is right over here: [https://openjdk.org/jeps/473][jeps473] + +## JEP-473 + +Stream Gatherers is a preview API delivered in JDK 22 that's in preview again +in JDK 23. Gatherers allow for stream pipelines to transform +data through intermediate operations instead of needing to perform +a terminating operation and generate a new stream. + +Today, if we want to process a list of elements, then perform an operation on the entire list, +and then continue processing the list, we'd have to do it like this: + +```java +void main(String[] args) { + var mylist = Stream.of(1,2,3) + .map(integer -> integer + 10) + .collect(Collectors.toList()); + // We can't perform any operations on the full list + // So we have to terminate the stream and spin up + // a new one, just to perform this simple operation + mylist.remove(1); + var myListMinusFirstElement = mylist.stream() + .map(integer -> integer + 20) + .toList(); + System.out.println(myListMinusFirstElement); +} +``` + +```bash +$ java --enable-preview demo +[31, 33] +``` + +With Gatherers, we're able to do this in a cleaner way by +doing our extra processing in an intermediate method! + +Ok before you look at this, it's not really `cleaner` per-say, but +it's a good example of how powerful these operations can really be! + +```java +void main(String[] args) { + + Supplier initializer = () -> new AtomicInteger(0); + + // Integrator + // 1 – the type of state used by this integrator + // 2 – the type of elements this integrator consumes + // 3 – the type of results this integrator can produce + Gatherer.Integrator integrator = + (state, element, downstream) -> { + int index = state.getAndIncrement(); + // We want to simply remove the second element from the list + if (index != 1) { + downstream.push(element); + } + return true; + }; + + var mylist = Stream.of(1, 2, 3) + .map(integer -> integer + 10) + .gather(Gatherer.ofSequential(initializer, integrator)) + .map(integer -> integer + 20) + .toList(); + System.out.println(mylist); +} +``` + +```bash + $ javac demo.java --enable-preview -source 23 + $ java --enable-preview demo + [31, 33] +``` + +From this simple example, we can see how extemely powerful this operation can be, allowing us +to bring in ANY state and mutate it, while also making filtering decisions. Since we can define +the output type, we can also perform mapping operations... Which is an excellent pivot into the next topic! + +## Gatherer Operations + +First, a quick terminology overview: + +Streams begin with a source (Stream.of() or myList.stream(), for example). Sources are followed by intermediate operations +such as map, filter, flatMap, etc. Finally, there is a terminating operation that closes the stream and emits a final object. + +There are simple terminating stream operators such as .toList or .toSet. However, Gatherers are similar to .collect. .collect +is a terminating stream operation, but it allows you to override a Collector and control the output of the stream. + +### Three Gatherer Functions + +Gatherers are designed to transform elements in a one-to-one, one-to-many, many-to-one or many-to-many fashion. + +#### Initializer + +The initializer function sets up a mechanism for storing data while +processing stream elements. A gatherer can use this to store the +current element and compare it to incoming elements, emitting only +the larger one. This effectively combines two input elements into one +output element. + +#### Integrator + +The integrator is responsible for processing incoming elements and +producing new output based on the current data and internal state. +It can terminate the process prematurely, such as a gatherer that +stops searching for the largest integer once it finds Integer.MAX_VALUE. + +#### Finisher + +The finisher function is executed once all input elements have been +processed. It can analyze the collected data and generate a final output. +For example, a gatherer searching for a specific element might throw an +exception if it's not found when the finisher is called. + +##### Examples + +This is an example of a basic mapping integrator. + +```java +// C is the type being consumed, P is the type being produced +Integrator integrator = + (state, element, downstream) -> { + // we can ignore the state for a simple mapper + var result = someMapperFunction.apply(element); + // We emit the result to the next iteration after applying the mapping + downstream.push(result); + // Continue to process items in the stream + return true; + }; +Gatherer.of(integrator); +``` + +Next, here is an example recreating the `.limit` stream operation, that uses an integrator and initializer + +```java +import java.util.concurrent.atomic.AtomicInteger; + +Supplier initializer = () -> new AtomicInteger(0); +Integrator integrator = + (state, element, downstream) -> { + // Edit our state for the current index + int currIndex = state.getAndIncrement(); + // Only push through results if within the stream size + if(currIndex < someStreamSize) { + downstream.push(element); + } + // Return whether the next index will be within stream size + return currIndex + 1 < someStreamSize; + }; +Gatherer.ofSequential(initializer, integrator); +``` + +## Built-In Gatherers + +TODO - add link to JavaDoc for each with examples! +The following are built-in gatherers in the [java.util.stream.Gatherers][gatherers-javadoc] class: + +fold is a stateful many-to-one gatherer which constructs an aggregate +incrementally and emits that aggregate when no more input elements exist. + +TODO - example + +mapConcurrent is a stateful one-to-one gatherer which invokes a supplied +function for each input element concurrently, up to a supplied limit. + +TODO - example + +scan is a stateful one-to-one gatherer which applies a supplied function +to the current state and the current element to produce the next element, +which it passes downstream. + +TODO - example + +windowFixed is a stateful many-to-many gatherer which groups input +elements into lists of a supplied size, emitting the windows +downstream when they are full. + +TODO - example + +windowSliding is a stateful many-to-many gatherer which groups input +elements into lists of a supplied size. After the first window, each +subsequent window is created from a copy of its predecessor by dropping +the first element and appending the next element from the input stream.. + +TODO - example + +## Gatherers are so useful, we can re-create every intermediate stream operation + +TODO - recreate everything as gatherers + +TODO - find real life example +- distinctBy +- grouping and applying a change to the whole collection + +[soby-chako]: https://github.com/sobychacko +[openjdk23]: https://openjdk.org/projects/jdk/23/ +[jeps473]: https://openjdk.org/jeps/473 +[gatherers-javadoc]: https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/util/stream/Gatherer.html diff --git a/_posts/2024-09-19-Easy-Spring-Rest-Client-with-OAuth2.markdown b/_posts/2024-09-19-Easy-Spring-Rest-Client-with-OAuth2.markdown index 9b4e7504..9cc05115 100644 --- a/_posts/2024-09-19-Easy-Spring-Rest-Client-with-OAuth2.markdown +++ b/_posts/2024-09-19-Easy-Spring-Rest-Client-with-OAuth2.markdown @@ -125,8 +125,7 @@ public class RestClientConfiguration @Bean public RestClient oauth2RestClient( - OAuth2AuthorizedClientManager authorizedClientManager, - LogbookClientHttpRequestInterceptor logbookClientHttpRequestInterceptor) { + OAuth2AuthorizedClientManager authorizedClientManager) { // This is the new class!!! We instantiate a new one and provide it the client registration to match OAuth2ClientHttpRequestInterceptor oAuth2ClientHttpRequestInterceptor =